diff --git a/.gitignore b/.gitignore index 853dca44b..dc154815f 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,9 @@ Thumbs.db node_modules/ bower_components/ +# Vagrant +.vagrant/ + # Some other random stuff always-ignore extensions *.diff diff --git a/.npmignore b/.npmignore index 8c3d2c8c5..810e48ef0 100644 --- a/.npmignore +++ b/.npmignore @@ -59,6 +59,9 @@ Thumbs.db # NPM packages folder. node_modules/ +# Vagrant +.vagrant/ + # Some other random stuff always-ignore extensions *.diff diff --git a/.travis.yml b/.travis.yml index 0e998a59e..a5ec7a4f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,11 @@ before_install: - "echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list" - "sudo apt-get update" - "sudo apt-get install mongodb-org-server" - + before_script: - "npm install" - export DISPLAY=:99.0 + - export COCO_TRAVIS_TEST=1 - sh -e /etc/init.d/xvfb start - "./node_modules/.bin/bower install" - "gem install sass" @@ -22,4 +23,4 @@ before_script: script: - "./node_modules/jasmine-node/bin/jasmine-node test/server/ --coffee --captureExceptions" - - "./node_modules/karma/bin/karma start --browsers Firefox --single-run" + # - "./node_modules/karma/bin/karma start --browsers Firefox --single-run --reporters progress" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42f4e7800..2ef5bf0b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,20 @@ -### Please sign our Contributor License Agreement +##Contributing to CodeCombat -**[http://codecombat.com/cla](http://codecombat.com/cla)** +There are many ways in which to contribute to CodeCombat, these include becoming a(n): + + - **[Archmage](https://codecombat.com/contribute/archmage)** (Coder) + - **[Artisan](https://codecombat.com/contribute/artisan)** (Level Builder) + - **[Adventurer](https://codecombat.com/contribute/adventurer)** (Level Playtester) + - **[Scribe](https://codecombat.com/contribute/scribe)** (Article Editor) + - **[Diplomat](https://codecombat.com/contribute/diplomat)** (Translator) + - **[Ambassador](https://codecombat.com/contribute/ambassador)** (Support) + +###The CLA + +If you do decide to contribute, we require you to sign a CLA (Contributor License Agreement). You can see it: + +**[https://codecombat.com/cla](https://codecombat.com/cla)** It just grants us a non-exclusive license to use your contribution and certifies you have the right to contribute the code you submit. For both our sakes, we need this before we can accept a pull request. Don't worry, it's super easy. -For more info, see [http://codecombat.com/legal](http://codecombat.com/legal). +For more info on the legal side of contributing to CodeCombat, see [https://codecombat.com/legal](https://codecombat.com/legal). diff --git a/LICENSE b/LICENSE index b0d5cb216..4c5b96475 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 CodeCombat Inc. and other contributors +Copyright (c) 2015 CodeCombat Inc. and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 3b69d0977..2d10a11ac 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,19 @@ -CodeCombat -========== +#CodeCombat - +<div style="text-align:center"><a href="http://codecombat.com/"><img src ="https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/readme_00.png" /></a></div> [](https://travis-ci.org/codecombat/codecombat) -CodeCombat is a multiplayer programming game for learning how to code. **See the [Archmage developer wiki](https://github.com/codecombat/codecombat/wiki/Archmage-Home) for a dev setup guide, extensive documentation, and much more.** +CodeCombat is a multiplayer programming game for learning how to code. **See the [Archmage (coder) developer wiki](https://github.com/codecombat/codecombat/wiki/Archmage-Home) for a dev setup guide, extensive documentation, and much more. Every new person that wants to start contributing the project coding should start there.** It's both a startup and a community project, completely open source under the [MIT and Creative Commons licenses](http://codecombat.com/legal). It's the largest open source [CoffeeScript](http://coffeescript.org/) project by lines of code, and since it's a game (with [really cool tech](https://github.com/codecombat/codecombat/wiki/Third-party-software-and-services)), it's really fun to hack on. Join us in teaching the world to code! Your contribution will go on to show millions of players how cool programming can be. -### [Getting Started](https://github.com/codecombat/codecombat/wiki/Developer-environment) +### [Getting Started](https://github.com/codecombat/codecombat/wiki/Dev-Setup:-General-Information) -We've made it easy to fork the project, run a simple script that'll install all the dependencies, and get a local copy of CodeCombat running right away on Mac, Linux, or Windows. See [the docs for details](https://github.com/codecombat/codecombat/wiki/Developer-environment). +We've made it easy to fork the project, run a simple script that'll install all the dependencies, and get a local copy of CodeCombat running right away on [Mac](https://github.com/codecombat/codecombat/wiki/Dev-Setup:-Mac-and-Vagrant), [Linux](https://github.com/codecombat/codecombat/wiki/Dev-Setup:-Linux), or [Windows](https://github.com/codecombat/codecombat/wiki/Dev-Setup:-Windows). See [the docs for details](https://github.com/codecombat/codecombat/wiki/Dev-Setup:-General-Information). ### [Getting In Touch](https://github.com/codecombat/codecombat/wiki/Developer-organization) -Whether you're novice or pro, the CodeCombat team is ready to help you implement your ideas. Reach out on our forum, our issue tracker, or our developer chat room, or see the docs for [more on how to contribute](https://github.com/codecombat/codecombat/wiki/Developer-organization). +Whether you're novice or pro, the CodeCombat team is ready to help you implement your ideas. Reach out on our [forum](http://discourse.codecombat.com), our [issue tracker](https://github.com/codecombat/codecombat/issues), or our [developer chat room](https://www.hipchat.com/g3plnOKqa), or see the docs for [more on how to contribute](https://github.com/codecombat/codecombat/wiki/Developer-organization). ### [License](https://github.com/codecombat/codecombat/blob/master/LICENSE) @@ -22,34 +21,58 @@ Whether you're novice or pro, the CodeCombat team is ready to help you implement ### [Join Us!](http://blog.codecombat.com/why-you-should-open-source-your-startup) +   - + +  +  - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ----------- - -[](http://www.google-melange.com/gsoc/homepage/google/gsoc2014) diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 000000000..6c338e4b8 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,29 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Original content copyright (c) 2014 dpen2000 licensed under the MIT license + +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + + # A VMware compatible box is avaliable from: + # https://github.com/spkane/vagrant-boxes/releases/download/v1.0.0/trusty64_vmware.box + config.vm.box = "ubuntu/trusty64" + + config.vm.network "forwarded_port", guest: 3000, host: 3000 + config.vm.network "forwarded_port", guest: 9485, host: 9485 + + config.vm.provision "shell", path: "scripts/vagrant/provision.sh" + + config.vm.provider "virtualbox" do |v| + v.memory = 2048 + v.cpus = 2 + end + + config.vm.provider "vmware_fusion" do |v| + v.memory = 2048 + v.cpus = 2 + end + +end diff --git a/app/assets/images/common/button-background-danger-active-border.png b/app/assets/images/common/button-background-danger-active-border.png new file mode 100644 index 000000000..30ed218dd Binary files /dev/null and b/app/assets/images/common/button-background-danger-active-border.png differ diff --git a/app/assets/images/common/button-background-danger-active.png b/app/assets/images/common/button-background-danger-active.png new file mode 100644 index 000000000..b198ed621 Binary files /dev/null and b/app/assets/images/common/button-background-danger-active.png differ diff --git a/app/assets/images/common/button-background-danger-disabled-border.png b/app/assets/images/common/button-background-danger-disabled-border.png new file mode 100644 index 000000000..eabd86d3f Binary files /dev/null and b/app/assets/images/common/button-background-danger-disabled-border.png differ diff --git a/app/assets/images/common/button-background-danger-disabled.png b/app/assets/images/common/button-background-danger-disabled.png new file mode 100644 index 000000000..494f85869 Binary files /dev/null and b/app/assets/images/common/button-background-danger-disabled.png differ diff --git a/app/assets/images/common/button-background-danger-pressed-border.png b/app/assets/images/common/button-background-danger-pressed-border.png new file mode 100644 index 000000000..eadb03397 Binary files /dev/null and b/app/assets/images/common/button-background-danger-pressed-border.png differ diff --git a/app/assets/images/common/button-background-danger-pressed.png b/app/assets/images/common/button-background-danger-pressed.png new file mode 100644 index 000000000..2438ad3ee Binary files /dev/null and b/app/assets/images/common/button-background-danger-pressed.png differ diff --git a/app/assets/images/common/button-background-primary-active-border.png b/app/assets/images/common/button-background-primary-active-border.png new file mode 100644 index 000000000..140858b50 Binary files /dev/null and b/app/assets/images/common/button-background-primary-active-border.png differ diff --git a/app/assets/images/common/button-background-primary-active.png b/app/assets/images/common/button-background-primary-active.png new file mode 100644 index 000000000..7e054911c Binary files /dev/null and b/app/assets/images/common/button-background-primary-active.png differ diff --git a/app/assets/images/common/button-background-primary-disabled-border.png b/app/assets/images/common/button-background-primary-disabled-border.png new file mode 100644 index 000000000..b3a0fedcf Binary files /dev/null and b/app/assets/images/common/button-background-primary-disabled-border.png differ diff --git a/app/assets/images/common/button-background-primary-disabled.png b/app/assets/images/common/button-background-primary-disabled.png new file mode 100644 index 000000000..adb402464 Binary files /dev/null and b/app/assets/images/common/button-background-primary-disabled.png differ diff --git a/app/assets/images/common/button-background-primary-pressed-border.png b/app/assets/images/common/button-background-primary-pressed-border.png new file mode 100644 index 000000000..2cb8f9059 Binary files /dev/null and b/app/assets/images/common/button-background-primary-pressed-border.png differ diff --git a/app/assets/images/common/button-background-primary-pressed.png b/app/assets/images/common/button-background-primary-pressed.png new file mode 100644 index 000000000..cc7ab716a Binary files /dev/null and b/app/assets/images/common/button-background-primary-pressed.png differ diff --git a/app/assets/images/common/button-background-success-active-border.png b/app/assets/images/common/button-background-success-active-border.png new file mode 100644 index 000000000..b64df9e49 Binary files /dev/null and b/app/assets/images/common/button-background-success-active-border.png differ diff --git a/app/assets/images/common/button-background-success-active.png b/app/assets/images/common/button-background-success-active.png new file mode 100644 index 000000000..bb6850e8b Binary files /dev/null and b/app/assets/images/common/button-background-success-active.png differ diff --git a/app/assets/images/common/button-background-success-inactive-border.png b/app/assets/images/common/button-background-success-inactive-border.png new file mode 100644 index 000000000..cf1dfc217 Binary files /dev/null and b/app/assets/images/common/button-background-success-inactive-border.png differ diff --git a/app/assets/images/common/button-background-success-inactive.png b/app/assets/images/common/button-background-success-inactive.png new file mode 100644 index 000000000..0e02036c2 Binary files /dev/null and b/app/assets/images/common/button-background-success-inactive.png differ diff --git a/app/assets/images/common/button-background-success-pressed-border.png b/app/assets/images/common/button-background-success-pressed-border.png new file mode 100644 index 000000000..c12979058 Binary files /dev/null and b/app/assets/images/common/button-background-success-pressed-border.png differ diff --git a/app/assets/images/common/button-background-success-pressed.png b/app/assets/images/common/button-background-success-pressed.png new file mode 100644 index 000000000..41c23a01f Binary files /dev/null and b/app/assets/images/common/button-background-success-pressed.png differ diff --git a/app/assets/images/common/button-background-warning-active-border.png b/app/assets/images/common/button-background-warning-active-border.png new file mode 100644 index 000000000..8a666e47b Binary files /dev/null and b/app/assets/images/common/button-background-warning-active-border.png differ diff --git a/app/assets/images/common/button-background-warning-active.png b/app/assets/images/common/button-background-warning-active.png new file mode 100644 index 000000000..eeff76f4f Binary files /dev/null and b/app/assets/images/common/button-background-warning-active.png differ diff --git a/app/assets/images/common/button-background-warning-disabled-border.png b/app/assets/images/common/button-background-warning-disabled-border.png new file mode 100644 index 000000000..62f12be18 Binary files /dev/null and b/app/assets/images/common/button-background-warning-disabled-border.png differ diff --git a/app/assets/images/common/button-background-warning-disabled.png b/app/assets/images/common/button-background-warning-disabled.png new file mode 100644 index 000000000..a0e743222 Binary files /dev/null and b/app/assets/images/common/button-background-warning-disabled.png differ diff --git a/app/assets/images/common/button-background-warning-pressed-border.png b/app/assets/images/common/button-background-warning-pressed-border.png new file mode 100644 index 000000000..ca2134e18 Binary files /dev/null and b/app/assets/images/common/button-background-warning-pressed-border.png differ diff --git a/app/assets/images/common/button-background-warning-pressed.png b/app/assets/images/common/button-background-warning-pressed.png new file mode 100644 index 000000000..21e6bd380 Binary files /dev/null and b/app/assets/images/common/button-background-warning-pressed.png differ diff --git a/app/assets/images/common/particles/bullet.png b/app/assets/images/common/particles/bullet.png new file mode 100644 index 000000000..8310ac5b9 Binary files /dev/null and b/app/assets/images/common/particles/bullet.png differ diff --git a/app/assets/images/common/particles/bullet2.png b/app/assets/images/common/particles/bullet2.png new file mode 100644 index 000000000..651fd98c2 Binary files /dev/null and b/app/assets/images/common/particles/bullet2.png differ diff --git a/app/assets/images/common/particles/cloud.png b/app/assets/images/common/particles/cloud.png new file mode 100644 index 000000000..c79d87506 Binary files /dev/null and b/app/assets/images/common/particles/cloud.png differ diff --git a/app/assets/images/common/particles/cloud_small.png b/app/assets/images/common/particles/cloud_small.png new file mode 100644 index 000000000..974e8ea21 Binary files /dev/null and b/app/assets/images/common/particles/cloud_small.png differ diff --git a/app/assets/images/common/particles/smoke.png b/app/assets/images/common/particles/smoke.png new file mode 100644 index 000000000..7bad8c685 Binary files /dev/null and b/app/assets/images/common/particles/smoke.png differ diff --git a/app/assets/images/common/particles/star.png b/app/assets/images/common/particles/star.png new file mode 100644 index 000000000..5e6f2a12f Binary files /dev/null and b/app/assets/images/common/particles/star.png differ diff --git a/app/assets/images/level/footer_background.jpg b/app/assets/images/level/footer_background.jpg new file mode 100644 index 000000000..97f24c48a Binary files /dev/null and b/app/assets/images/level/footer_background.jpg differ diff --git a/app/assets/images/level/loading_bar_back.png b/app/assets/images/level/loading_bar_back.png new file mode 100644 index 000000000..140d90d9c Binary files /dev/null and b/app/assets/images/level/loading_bar_back.png differ diff --git a/app/assets/images/level/loading_bar_fill.png b/app/assets/images/level/loading_bar_fill.png new file mode 100644 index 000000000..47e9770a7 Binary files /dev/null and b/app/assets/images/level/loading_bar_fill.png differ diff --git a/app/assets/images/level/loading_bar_rim.png b/app/assets/images/level/loading_bar_rim.png new file mode 100644 index 000000000..b5155e62d Binary files /dev/null and b/app/assets/images/level/loading_bar_rim.png differ diff --git a/app/assets/images/level/loading_left_wing.png b/app/assets/images/level/loading_left_wing.png deleted file mode 100644 index 36d91671a..000000000 Binary files a/app/assets/images/level/loading_left_wing.png and /dev/null differ diff --git a/app/assets/images/level/loading_left_wing_1366.jpg b/app/assets/images/level/loading_left_wing_1366.jpg new file mode 100644 index 000000000..29fe67e9c Binary files /dev/null and b/app/assets/images/level/loading_left_wing_1366.jpg differ diff --git a/app/assets/images/level/loading_left_wing_1920.jpg b/app/assets/images/level/loading_left_wing_1920.jpg new file mode 100644 index 000000000..73aab0978 Binary files /dev/null and b/app/assets/images/level/loading_left_wing_1920.jpg differ diff --git a/app/assets/images/level/loading_right_wing.png b/app/assets/images/level/loading_right_wing.png deleted file mode 100644 index 36dedada4..000000000 Binary files a/app/assets/images/level/loading_right_wing.png and /dev/null differ diff --git a/app/assets/images/level/loading_right_wing_1366.jpg b/app/assets/images/level/loading_right_wing_1366.jpg new file mode 100644 index 000000000..963feba9b Binary files /dev/null and b/app/assets/images/level/loading_right_wing_1366.jpg differ diff --git a/app/assets/images/level/loading_right_wing_1920.jpg b/app/assets/images/level/loading_right_wing_1920.jpg new file mode 100644 index 000000000..2fe4465ce Binary files /dev/null and b/app/assets/images/level/loading_right_wing_1920.jpg differ diff --git a/app/assets/images/level/popover_background.png b/app/assets/images/level/popover_background.png index 214edeaae..f87ea6d4b 100644 Binary files a/app/assets/images/level/popover_background.png and b/app/assets/images/level/popover_background.png differ diff --git a/app/assets/images/level/popover_border_background.png b/app/assets/images/level/popover_border_background.png new file mode 100644 index 000000000..a8414487b Binary files /dev/null and b/app/assets/images/level/popover_border_background.png differ diff --git a/app/assets/images/pages/about/cat_small.png b/app/assets/images/pages/about/cat_small.png new file mode 100644 index 000000000..312607708 Binary files /dev/null and b/app/assets/images/pages/about/cat_small.png differ diff --git a/app/assets/images/pages/about/jose_small.png b/app/assets/images/pages/about/jose_small.png new file mode 100644 index 000000000..01bb440a9 Binary files /dev/null and b/app/assets/images/pages/about/jose_small.png differ diff --git a/app/assets/images/pages/about/josh_small.png b/app/assets/images/pages/about/josh_small.png new file mode 100644 index 000000000..80daa5c2a Binary files /dev/null and b/app/assets/images/pages/about/josh_small.png differ diff --git a/app/assets/images/pages/about/oleg_small.png b/app/assets/images/pages/about/oleg_small.png new file mode 100644 index 000000000..4690ed5cc Binary files /dev/null and b/app/assets/images/pages/about/oleg_small.png differ diff --git a/app/assets/images/pages/about/pavel_small.png b/app/assets/images/pages/about/pavel_small.png new file mode 100644 index 000000000..336e3e6e6 Binary files /dev/null and b/app/assets/images/pages/about/pavel_small.png differ diff --git a/app/assets/images/pages/base/logo_square_120.png b/app/assets/images/pages/base/logo_square_120.png new file mode 100644 index 000000000..08f3c3eff Binary files /dev/null and b/app/assets/images/pages/base/logo_square_120.png differ diff --git a/app/assets/images/pages/base/logo_square_250.png b/app/assets/images/pages/base/logo_square_250.png index cb9b12b4c..7f2b50e3b 100644 Binary files a/app/assets/images/pages/base/logo_square_250.png and b/app/assets/images/pages/base/logo_square_250.png differ diff --git a/app/assets/images/pages/clans/dashboard_preview.png b/app/assets/images/pages/clans/dashboard_preview.png new file mode 100644 index 000000000..d45f8911f Binary files /dev/null and b/app/assets/images/pages/clans/dashboard_preview.png differ diff --git a/app/assets/images/pages/contribute/class_detail_adventurer.png b/app/assets/images/pages/contribute/class_detail_adventurer.png new file mode 100644 index 000000000..2e738ae2d Binary files /dev/null and b/app/assets/images/pages/contribute/class_detail_adventurer.png differ diff --git a/app/assets/images/pages/contribute/class_detail_ambassador.png b/app/assets/images/pages/contribute/class_detail_ambassador.png new file mode 100644 index 000000000..632cb527b Binary files /dev/null and b/app/assets/images/pages/contribute/class_detail_ambassador.png differ diff --git a/app/assets/images/pages/contribute/class_detail_archmage.png b/app/assets/images/pages/contribute/class_detail_archmage.png new file mode 100644 index 000000000..25b0145bd Binary files /dev/null and b/app/assets/images/pages/contribute/class_detail_archmage.png differ diff --git a/app/assets/images/pages/contribute/class_detail_artisan.png b/app/assets/images/pages/contribute/class_detail_artisan.png new file mode 100644 index 000000000..9d4f001e3 Binary files /dev/null and b/app/assets/images/pages/contribute/class_detail_artisan.png differ diff --git a/app/assets/images/pages/contribute/class_detail_diplomat.png b/app/assets/images/pages/contribute/class_detail_diplomat.png new file mode 100644 index 000000000..d79d59bdf Binary files /dev/null and b/app/assets/images/pages/contribute/class_detail_diplomat.png differ diff --git a/app/assets/images/pages/contribute/class_detail_scribe.png b/app/assets/images/pages/contribute/class_detail_scribe.png new file mode 100644 index 000000000..a9378b41c Binary files /dev/null and b/app/assets/images/pages/contribute/class_detail_scribe.png differ diff --git a/app/assets/images/pages/contribute/tile_adventurer.png b/app/assets/images/pages/contribute/tile_adventurer.png new file mode 100644 index 000000000..6b6a8b5eb Binary files /dev/null and b/app/assets/images/pages/contribute/tile_adventurer.png differ diff --git a/app/assets/images/pages/contribute/tile_ambassador.png b/app/assets/images/pages/contribute/tile_ambassador.png new file mode 100644 index 000000000..a36b2ca5b Binary files /dev/null and b/app/assets/images/pages/contribute/tile_ambassador.png differ diff --git a/app/assets/images/pages/contribute/tile_archmage.png b/app/assets/images/pages/contribute/tile_archmage.png new file mode 100644 index 000000000..bb7659713 Binary files /dev/null and b/app/assets/images/pages/contribute/tile_archmage.png differ diff --git a/app/assets/images/pages/contribute/tile_artisan.png b/app/assets/images/pages/contribute/tile_artisan.png new file mode 100644 index 000000000..53b00e73a Binary files /dev/null and b/app/assets/images/pages/contribute/tile_artisan.png differ diff --git a/app/assets/images/pages/contribute/tile_diplomat.png b/app/assets/images/pages/contribute/tile_diplomat.png new file mode 100644 index 000000000..46704bc0a Binary files /dev/null and b/app/assets/images/pages/contribute/tile_diplomat.png differ diff --git a/app/assets/images/pages/contribute/tile_scribe.png b/app/assets/images/pages/contribute/tile_scribe.png new file mode 100644 index 000000000..3ee5a429a Binary files /dev/null and b/app/assets/images/pages/contribute/tile_scribe.png differ diff --git a/app/assets/images/pages/courses/101_info.png b/app/assets/images/pages/courses/101_info.png new file mode 100644 index 000000000..563034f61 Binary files /dev/null and b/app/assets/images/pages/courses/101_info.png differ diff --git a/app/assets/images/pages/courses/102_info.png b/app/assets/images/pages/courses/102_info.png new file mode 100644 index 000000000..93370582d Binary files /dev/null and b/app/assets/images/pages/courses/102_info.png differ diff --git a/app/assets/images/pages/courses/103_info.png b/app/assets/images/pages/courses/103_info.png new file mode 100644 index 000000000..04ead0bad Binary files /dev/null and b/app/assets/images/pages/courses/103_info.png differ diff --git a/app/assets/images/pages/courses/104_info.png b/app/assets/images/pages/courses/104_info.png new file mode 100644 index 000000000..d953c89e9 Binary files /dev/null and b/app/assets/images/pages/courses/104_info.png differ diff --git a/app/assets/images/pages/courses/105_info.png b/app/assets/images/pages/courses/105_info.png new file mode 100644 index 000000000..d19e38e3b Binary files /dev/null and b/app/assets/images/pages/courses/105_info.png differ diff --git a/app/assets/images/pages/courses/106_info.png b/app/assets/images/pages/courses/106_info.png new file mode 100644 index 000000000..5e7aa3a65 Binary files /dev/null and b/app/assets/images/pages/courses/106_info.png differ diff --git a/app/assets/images/pages/courses/107_info.png b/app/assets/images/pages/courses/107_info.png new file mode 100644 index 000000000..fa0e92b14 Binary files /dev/null and b/app/assets/images/pages/courses/107_info.png differ diff --git a/app/assets/images/pages/front/play_web.jpg b/app/assets/images/pages/front/play_web.jpg deleted file mode 100644 index 6ef8412da..000000000 Binary files a/app/assets/images/pages/front/play_web.jpg and /dev/null differ diff --git a/app/assets/images/pages/game-menu/lock-processed.png b/app/assets/images/pages/game-menu/lock-processed.png new file mode 100644 index 000000000..bb038b4b0 Binary files /dev/null and b/app/assets/images/pages/game-menu/lock-processed.png differ diff --git a/app/assets/images/pages/modal/auth/extra-pane.png b/app/assets/images/pages/modal/auth/extra-pane.png new file mode 100644 index 000000000..83c8d6ee5 Binary files /dev/null and b/app/assets/images/pages/modal/auth/extra-pane.png differ diff --git a/app/assets/images/pages/modal/auth/login-background.png b/app/assets/images/pages/modal/auth/login-background.png new file mode 100644 index 000000000..00ee8341f Binary files /dev/null and b/app/assets/images/pages/modal/auth/login-background.png differ diff --git a/app/assets/images/pages/modal/auth/signup-background.png b/app/assets/images/pages/modal/auth/signup-background.png new file mode 100644 index 000000000..add84857b Binary files /dev/null and b/app/assets/images/pages/modal/auth/signup-background.png differ diff --git a/app/assets/images/pages/play/campaign-banner.png b/app/assets/images/pages/play/campaign-banner.png new file mode 100644 index 000000000..0ad6af9a0 Binary files /dev/null and b/app/assets/images/pages/play/campaign-banner.png differ diff --git a/app/assets/images/pages/play/level-banner-complete.png b/app/assets/images/pages/play/level-banner-complete.png new file mode 100644 index 000000000..4ea601195 Binary files /dev/null and b/app/assets/images/pages/play/level-banner-complete.png differ diff --git a/app/assets/images/pages/play/level-banner-started.png b/app/assets/images/pages/play/level-banner-started.png new file mode 100644 index 000000000..c84d89526 Binary files /dev/null and b/app/assets/images/pages/play/level-banner-started.png differ diff --git a/app/assets/images/pages/play/level-info-background.png b/app/assets/images/pages/play/level-info-background.png new file mode 100644 index 000000000..6b4c847ee Binary files /dev/null and b/app/assets/images/pages/play/level-info-background.png differ diff --git a/app/assets/images/pages/play/level-info-status-spritesheet.png b/app/assets/images/pages/play/level-info-status-spritesheet.png new file mode 100644 index 000000000..1f6b1f30c Binary files /dev/null and b/app/assets/images/pages/play/level-info-status-spritesheet.png differ diff --git a/app/assets/images/pages/play/level/modal/achievement_plate.png b/app/assets/images/pages/play/level/modal/achievement_plate.png deleted file mode 100644 index 1ab5a60b3..000000000 Binary files a/app/assets/images/pages/play/level/modal/achievement_plate.png and /dev/null differ diff --git a/app/assets/images/pages/play/level/modal/reward_plate.png b/app/assets/images/pages/play/level/modal/reward_plate.png index 3ee2bba20..0a0a7b6e7 100644 Binary files a/app/assets/images/pages/play/level/modal/reward_plate.png and b/app/assets/images/pages/play/level/modal/reward_plate.png differ diff --git a/app/assets/images/pages/play/level/modal/reward_plate_wide.png b/app/assets/images/pages/play/level/modal/reward_plate_wide.png index 466c0af7f..d99ae8fac 100644 Binary files a/app/assets/images/pages/play/level/modal/reward_plate_wide.png and b/app/assets/images/pages/play/level/modal/reward_plate_wide.png differ diff --git a/app/assets/images/pages/play/level/modal/victory_hero.png b/app/assets/images/pages/play/level/modal/victory_hero.png new file mode 100644 index 000000000..068af4688 Binary files /dev/null and b/app/assets/images/pages/play/level/modal/victory_hero.png differ diff --git a/app/assets/images/pages/play/level/modal/victory_modal_background.png b/app/assets/images/pages/play/level/modal/victory_modal_background.png index 11f8ad8f1..7ed30bcb4 100644 Binary files a/app/assets/images/pages/play/level/modal/victory_modal_background.png and b/app/assets/images/pages/play/level/modal/victory_modal_background.png differ diff --git a/app/assets/images/pages/play/level/modal/victory_modal_blue_banner.png b/app/assets/images/pages/play/level/modal/victory_modal_blue_banner.png deleted file mode 100644 index 0c87583bf..000000000 Binary files a/app/assets/images/pages/play/level/modal/victory_modal_blue_banner.png and /dev/null differ diff --git a/app/assets/images/pages/play/level/modal/victory_modal_border_background.png b/app/assets/images/pages/play/level/modal/victory_modal_border_background.png new file mode 100644 index 000000000..00657032c Binary files /dev/null and b/app/assets/images/pages/play/level/modal/victory_modal_border_background.png differ diff --git a/app/assets/images/pages/play/level/modal/victory_modal_shelf.png b/app/assets/images/pages/play/level/modal/victory_modal_shelf.png new file mode 100644 index 000000000..bd8586443 Binary files /dev/null and b/app/assets/images/pages/play/level/modal/victory_modal_shelf.png differ diff --git a/app/assets/images/pages/play/level/modal/victory_word.png b/app/assets/images/pages/play/level/modal/victory_word.png index 60563a588..0ead423a1 100644 Binary files a/app/assets/images/pages/play/level/modal/victory_word.png and b/app/assets/images/pages/play/level/modal/victory_word.png differ diff --git a/app/assets/images/pages/play/level/modal/xp_gems_parchment.png b/app/assets/images/pages/play/level/modal/xp_gems_parchment.png new file mode 100644 index 000000000..917db106f Binary files /dev/null and b/app/assets/images/pages/play/level/modal/xp_gems_parchment.png differ diff --git a/app/assets/images/pages/play/map_dungeon.jpg b/app/assets/images/pages/play/map_dungeon.jpg deleted file mode 100644 index 2f5af9992..000000000 Binary files a/app/assets/images/pages/play/map_dungeon.jpg and /dev/null differ diff --git a/app/assets/images/pages/play/map_dungeon_icon.jpg b/app/assets/images/pages/play/map_dungeon_icon.jpg deleted file mode 100644 index f757ba486..000000000 Binary files a/app/assets/images/pages/play/map_dungeon_icon.jpg and /dev/null differ diff --git a/app/assets/images/pages/play/map_forest.jpg b/app/assets/images/pages/play/map_forest.jpg deleted file mode 100644 index 51d50779c..000000000 Binary files a/app/assets/images/pages/play/map_forest.jpg and /dev/null differ diff --git a/app/assets/images/pages/play/map_forest_icon.jpg b/app/assets/images/pages/play/map_forest_icon.jpg deleted file mode 100644 index 2a86d18ff..000000000 Binary files a/app/assets/images/pages/play/map_forest_icon.jpg and /dev/null differ diff --git a/app/assets/images/pages/play/menu_icons.png b/app/assets/images/pages/play/menu_icons.png index 53028d44b..37cd5fc03 100644 Binary files a/app/assets/images/pages/play/menu_icons.png and b/app/assets/images/pages/play/menu_icons.png differ diff --git a/app/assets/images/pages/play/modal/buy-gems-background.png b/app/assets/images/pages/play/modal/buy-gems-background.png index 563124aeb..59b61d07f 100644 Binary files a/app/assets/images/pages/play/modal/buy-gems-background.png and b/app/assets/images/pages/play/modal/buy-gems-background.png differ diff --git a/app/assets/images/pages/play/modal/leaderboard-background.png b/app/assets/images/pages/play/modal/leaderboard-background.png new file mode 100644 index 000000000..28d3a8260 Binary files /dev/null and b/app/assets/images/pages/play/modal/leaderboard-background.png differ diff --git a/app/assets/images/pages/play/modal/parental_nudge_wizard.png b/app/assets/images/pages/play/modal/parental_nudge_wizard.png new file mode 100644 index 000000000..cf98ead1a Binary files /dev/null and b/app/assets/images/pages/play/modal/parental_nudge_wizard.png differ diff --git a/app/assets/images/pages/play/modal/parental_prompt_modal_background.png b/app/assets/images/pages/play/modal/parental_prompt_modal_background.png new file mode 100644 index 000000000..bab86a3ca Binary files /dev/null and b/app/assets/images/pages/play/modal/parental_prompt_modal_background.png differ diff --git a/app/assets/images/pages/play/modal/random-gems-background.png b/app/assets/images/pages/play/modal/random-gems-background.png new file mode 100644 index 000000000..6ca966d12 Binary files /dev/null and b/app/assets/images/pages/play/modal/random-gems-background.png differ diff --git a/app/assets/images/pages/play/modal/subscribe-background-blank.png b/app/assets/images/pages/play/modal/subscribe-background-blank.png new file mode 100644 index 000000000..8c13a3534 Binary files /dev/null and b/app/assets/images/pages/play/modal/subscribe-background-blank.png differ diff --git a/app/assets/images/pages/play/modal/subscribe-background.png b/app/assets/images/pages/play/modal/subscribe-background.png new file mode 100644 index 000000000..eab6b42ac Binary files /dev/null and b/app/assets/images/pages/play/modal/subscribe-background.png differ diff --git a/app/assets/images/pages/play/modal/subscribe-gems.png b/app/assets/images/pages/play/modal/subscribe-gems.png new file mode 100644 index 000000000..9f22df75c Binary files /dev/null and b/app/assets/images/pages/play/modal/subscribe-gems.png differ diff --git a/app/assets/images/pages/play/modal/subscribe-heroes.png b/app/assets/images/pages/play/modal/subscribe-heroes.png new file mode 100644 index 000000000..bd55cf02b Binary files /dev/null and b/app/assets/images/pages/play/modal/subscribe-heroes.png differ diff --git a/app/assets/images/pages/play/play-spritesheet.png b/app/assets/images/pages/play/play-spritesheet.png new file mode 100644 index 000000000..7d40446b5 Binary files /dev/null and b/app/assets/images/pages/play/play-spritesheet.png differ diff --git a/app/assets/images/pages/play/portal-background.png b/app/assets/images/pages/play/portal-background.png new file mode 100644 index 000000000..4a9d33933 Binary files /dev/null and b/app/assets/images/pages/play/portal-background.png differ diff --git a/app/assets/images/pages/play/portal-campaigns.png b/app/assets/images/pages/play/portal-campaigns.png new file mode 100644 index 000000000..3d9806dd1 Binary files /dev/null and b/app/assets/images/pages/play/portal-campaigns.png differ diff --git a/app/assets/images/pages/play/star.png b/app/assets/images/pages/play/star.png new file mode 100644 index 000000000..52737ebe1 Binary files /dev/null and b/app/assets/images/pages/play/star.png differ diff --git a/app/assets/javascripts/mock-ajax.js b/app/assets/javascripts/mock-ajax.js deleted file mode 100644 index e4b75ed7b..000000000 --- a/app/assets/javascripts/mock-ajax.js +++ /dev/null @@ -1,328 +0,0 @@ -/* - -Jasmine-Ajax : a set of helpers for testing AJAX requests under the Jasmine -BDD framework for JavaScript. - -http://github.com/pivotal/jasmine-ajax - -Jasmine Home page: http://pivotal.github.com/jasmine - -Copyright (c) 2008-2013 Pivotal Labs - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -(function() { - function extend(destination, source, propertiesToSkip) { - propertiesToSkip = propertiesToSkip || []; - for (var property in source) { - if (!arrayContains(propertiesToSkip, property)) { - destination[property] = source[property]; - } - } - return destination; - } - - function arrayContains(arr, item) { - for (var i = 0; i < arr.length; i++) { - if (arr[i] === item) { - return true; - } - } - return false; - } - - function MockAjax(global) { - var requestTracker = new RequestTracker(), - stubTracker = new StubTracker(), - realAjaxFunction = global.XMLHttpRequest, - mockAjaxFunction = fakeRequest(requestTracker, stubTracker); - - this.install = function() { - global.XMLHttpRequest = mockAjaxFunction; - }; - - this.uninstall = function() { - global.XMLHttpRequest = realAjaxFunction; - - this.stubs.reset(); - this.requests.reset(); - }; - - this.stubRequest = function(url, data) { - var stub = new RequestStub(url, data); - stubTracker.addStub(stub); - return stub; - }; - - this.withMock = function(closure) { - this.install(); - try { - closure(); - } finally { - this.uninstall(); - } - }; - - this.requests = requestTracker; - this.stubs = stubTracker; - } - - function StubTracker() { - var stubs = []; - - this.addStub = function(stub) { - stubs.push(stub); - }; - - this.reset = function() { - stubs = []; - }; - - this.findStub = function(url, data) { - for (var i = stubs.length - 1; i >= 0; i--) { - var stub = stubs[i]; - if (stub.matches(url, data)) { - return stub; - } - } - }; - } - - function fakeRequest(requestTracker, stubTracker) { - function FakeXMLHttpRequest() { - requestTracker.track(this); - this.requestHeaders = {}; - } - - var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout']; - extend(FakeXMLHttpRequest.prototype, new window.XMLHttpRequest(), iePropertiesThatCannotBeCopied); - extend(FakeXMLHttpRequest.prototype, { - open: function() { - this.method = arguments[0]; - this.url = arguments[1]; - this.username = arguments[3]; - this.password = arguments[4]; - this.readyState = 1; - this.onreadystatechange(); - }, - - setRequestHeader: function(header, value) { - this.requestHeaders[header] = value; - }, - - abort: function() { - this.readyState = 0; - this.status = 0; - this.statusText = "abort"; - this.onreadystatechange(); - }, - - readyState: 0, - - onload: function() { - }, - - onreadystatechange: function(isTimeout) { - }, - - status: null, - - send: function(data) { - this.params = data; - this.readyState = 2; - this.onreadystatechange(); - - var stub = stubTracker.findStub(this.url, data); - if (stub) { - this.response(stub); - } - }, - - data: function() { - var data = {}; - if (typeof this.params !== 'string') { return data; } - var params = this.params.split('&'); - - for (var i = 0; i < params.length; ++i) { - var kv = params[i].replace(/\+/g, ' ').split('='); - var key = decodeURIComponent(kv[0]); - data[key] = data[key] || []; - data[key].push(decodeURIComponent(kv[1])); - } - return data; - }, - - getResponseHeader: function(name) { - return this.responseHeaders[name]; - }, - - getAllResponseHeaders: function() { - var responseHeaders = []; - for (var i in this.responseHeaders) { - if (this.responseHeaders.hasOwnProperty(i)) { - responseHeaders.push(i + ': ' + this.responseHeaders[i]); - } - } - return responseHeaders.join('\r\n'); - }, - - responseText: null, - - response: function(response) { - this.status = response.status; - this.statusText = response.statusText || ""; - this.responseText = response.responseText || ""; - this.readyState = 4; - this.responseHeaders = response.responseHeaders || - {"Content-type": response.contentType || "application/json" }; - - this.onload(); - this.onreadystatechange(); - }, - - responseTimeout: function() { - this.readyState = 4; - jasmine.clock().tick(30000); - this.onreadystatechange('timeout'); - } - }); - - return FakeXMLHttpRequest; - } - - function RequestTracker() { - var requests = []; - - this.track = function(request) { - requests.push(request); - }; - - this.first = function() { - return requests[0]; - }; - - this.count = function() { - return requests.length; - }; - - this.reset = function() { - requests = []; - }; - - this.mostRecent = function() { - return requests[requests.length - 1]; - }; - - this.at = function(index) { - return requests[index]; - }; - - this.all = function() { - return requests.slice(0); - }; - - this.filter = function(url_to_match) { - if (requests.length == 0) return []; - var matching_requests = []; - - for (var i = 0; i < requests.length; i++) { - if (url_to_match instanceof RegExp && - url_to_match.test(requests[i].url)) { - matching_requests.push(requests[i]); - } else if (url_to_match instanceof Function && - url_to_match(requests[i])) { - matching_requests.push(requests[i]); - } else { - if (requests[i].url == url_to_match) { - matching_requests.push(requests[i]); - } - } - } - - return matching_requests; - }; - - this.sendResponses = function(responseMap) { - var urls = Object.keys(responseMap); - var success = true; - for(var i in urls) { - var url = urls[i]; - var responseBody = responseMap[url]; - var responded = false; - - var requests = jasmine.Ajax.requests.all().slice(); - for(var j in requests) { - var request = requests[j]; - if(request.url.startsWith(url)) { - request.response({status: 200, responseText: JSON.stringify(responseBody)}); - responded = true; - break; - } - } - if(!responded) { - var allRequests = jasmine.Ajax.requests.all(); - urls = []; - for(var k in allRequests) urls.push(allRequests[k].url); - console.error('could not find response for', url, 'in', urls, allRequests); - success = false; - continue; - } - } - return success; - } - } - - function RequestStub(url, stubData) { - var split = url.split('?'); - this.url = split[0]; - - var normalizeQuery = function(query) { - return query ? query.split('&').sort().join('&') : undefined; - }; - - this.query = normalizeQuery(split[1]); - this.data = normalizeQuery(stubData); - - this.andReturn = function(options) { - this.status = options.status || 200; - - this.contentType = options.contentType; - this.responseText = options.responseText; - }; - - this.matches = function(fullUrl, data) { - var urlSplit = fullUrl.split('?'), - url = urlSplit[0], - query = urlSplit[1]; - return this.url === url && this.query === normalizeQuery(query) && (!this.data || this.data === normalizeQuery(data)); - }; - } - - if (typeof window === "undefined" && typeof exports === "object") { - exports.MockAjax = MockAjax; - jasmine.Ajax = new MockAjax(exports); - } else { - window.MockAjax = MockAjax; - jasmine.Ajax = new MockAjax(window); - } -}()); - diff --git a/app/assets/javascripts/run-tests.js b/app/assets/javascripts/run-tests.js index 7dbdf9859..a32c38e6e 100644 --- a/app/assets/javascripts/run-tests.js +++ b/app/assets/javascripts/run-tests.js @@ -1,7 +1,10 @@ // Helper for running tests through Karma. // Hooks into the test view logic for running tests. -require('core/initialize'); + +window.userObject = {_id:'1'} +initialize = require('core/initialize'); +initialize.init(); console.debug = function() {}; // Karma conf doesn't seem to work? Debug messages are still emitted when they shouldn't be. TestView = require('views/TestView'); -TestView.runTests(); \ No newline at end of file +TestView.runTests(); diff --git a/app/assets/javascripts/workers/aether_worker.js b/app/assets/javascripts/workers/aether_worker.js index a1d8e3038..560b70c4e 100644 --- a/app/assets/javascripts/workers/aether_worker.js +++ b/app/assets/javascripts/workers/aether_worker.js @@ -4,9 +4,16 @@ var Global = self; importScripts("/javascripts/lodash.js", "/javascripts/aether.js"); //console.log("Aether Tome worker has finished importing scripts."); var aethers = {}; +var languagesImported = {}; -var createAether = function (spellKey, options) -{ +var ensureLanguageImported = function(language) { + if (languagesImported[language]) return; + importScripts("/javascripts/app/vendor/aether-" + language + ".js"); + languagesImported[language] = true; +}; + +var createAether = function (spellKey, options) { + ensureLanguageImported(options.language); aethers[spellKey] = new Aether(options); return JSON.stringify({ "message": "Created aether for " + spellKey, @@ -15,7 +22,6 @@ var createAether = function (spellKey, options) }; var hasChangedSignificantly = function(spellKey, a,b,careAboutLineNumbers,careAboutLint) { - var hasChanged = aethers[spellKey].hasChangedSignificantly(a,b,careAboutLineNumbers,careAboutLint); var functionName = "hasChangedSignificantly"; var returnObject = { @@ -26,8 +32,8 @@ var hasChangedSignificantly = function(spellKey, a,b,careAboutLineNumbers,careAb return JSON.stringify(returnObject); }; -var updateLanguageAether = function(newLanguage) -{ +var updateLanguageAether = function(newLanguage) { + ensureLanguageImported(newLanguage); for (var spellKey in aethers) { if (aethers.hasOwnProperty(spellKey)) @@ -38,8 +44,7 @@ var updateLanguageAether = function(newLanguage) } }; -var lint = function(spellKey, source) -{ +var lint = function(spellKey, source) { var currentAether = aethers[spellKey]; var lintMessages = currentAether.lint(source); var functionName = "lint"; @@ -50,8 +55,7 @@ var lint = function(spellKey, source) return JSON.stringify(returnObject); }; -var transpile = function(spellKey, source) -{ +var transpile = function(spellKey, source) { var currentAether = aethers[spellKey]; currentAether.transpile(source); var functionName = "transpile"; @@ -62,6 +66,7 @@ var transpile = function(spellKey, source) }; return JSON.stringify(returnObject); }; + self.addEventListener('message', function(e) { var data = JSON.parse(e.data); if (data.function == "createAether") @@ -70,7 +75,7 @@ self.addEventListener('message', function(e) { } else if (data.function == "updateLanguageAether") { - updateLanguageAether(data.newLanguage) + updateLanguageAether(data.newLanguage); } else if (data.function == "hasChangedSignificantly") { diff --git a/app/assets/javascripts/workers/worker_world.js b/app/assets/javascripts/workers/worker_world.js index 623f6b334..4b5f43f07 100644 --- a/app/assets/javascripts/workers/worker_world.js +++ b/app/assets/javascripts/workers/worker_world.js @@ -43,7 +43,7 @@ var console = { var args = [].slice.call(arguments); for(var i = 0; i < args.length; ++i) { if(args[i] && args[i].constructor) { - if(args[i].constructor.className === "Thang" || args[i].isComponent) + if(args[i].constructor.className === "Thang" || args[i].isComponent || args[i].isVector || args[i].isRectangle || args[i].isEllipse) args[i] = args[i].toString(); } } @@ -64,6 +64,24 @@ console.error = console.warn = console.info = console.debug = console.log; self.console = console; self.importScripts('/javascripts/lodash.js', '/javascripts/world.js', '/javascripts/aether.js'); +var myImportScripts = importScripts; + +var languagesImported = {}; +var ensureLanguageImported = function(language) { + if (languagesImported[language]) return; + if (language === 'javascript') return; // Only has JSHint, but we don't need to lint here. + myImportScripts("/javascripts/app/vendor/aether-" + language + ".js"); + languagesImported[language] = true; +}; + +var ensureLanguagesImportedFromUserCodeMap = function (userCodeMap) { + for (var thangID in userCodeMap) + for (var spellName in userCodeMap[thangID]) { + var language = userCodeMap[thangID][spellName].originalOptions.language; + ensureLanguageImported(language); + } +}; + var restricted = ["XMLHttpRequest", "Worker"]; if (!self.navigator || !(self.navigator.userAgent.indexOf('MSIE') > 0) && @@ -261,12 +279,13 @@ self.retrieveValueFromFrame = function retrieveValueFromFrame(args) { self.enableFlowOnThangSpell = function (thangID, spellID, userCodeMap) { try { var options = userCodeMap[thangID][spellID].originalOptions; - if (options.includeFlow === true && options.noSerializationInFlow === true) + if (options.includeFlow === true && options.noSerializationInFlow === true && options.noVariablesInFlow === false) return; else { options.includeFlow = true; options.noSerializationInFlow = true; + options.noVariablesInFlow = false; var temporaryAether = Aether.deserialize(userCodeMap[thangID][spellID]); temporaryAether.transpile(temporaryAether.raw); userCodeMap[thangID][spellID] = temporaryAether.serialize(); @@ -283,6 +302,7 @@ self.setupDebugWorldToRunUntilFrame = function (args) { self.debugt0 = new Date(); self.logsLogged = 0; + ensureLanguagesImportedFromUserCodeMap(args.userCodeMap); var stringifiedUserCodeMap = JSON.stringify(args.userCodeMap); var userCodeMapHasChanged = ! _.isEqual(self.currentUserCodeMapCopy, stringifiedUserCodeMap); self.currentUserCodeMapCopy = stringifiedUserCodeMap; @@ -292,6 +312,7 @@ self.setupDebugWorldToRunUntilFrame = function (args) { self.debugWorld.levelSessionIDs = args.levelSessionIDs; self.debugWorld.submissionCount = args.submissionCount; self.debugWorld.flagHistory = args.flagHistory; + self.debugWorld.difficulty = args.difficulty; if (args.level) self.debugWorld.loadFromLevel(args.level, true); self.debugWorld.debugging = true; @@ -347,10 +368,12 @@ self.runWorld = function runWorld(args) { self.logsLogged = 0; try { + ensureLanguagesImportedFromUserCodeMap(args.userCodeMap); self.world = new World(args.userCodeMap); self.world.levelSessionIDs = args.levelSessionIDs; self.world.submissionCount = args.submissionCount; self.world.flagHistory = args.flagHistory || []; + self.world.difficulty = args.difficulty || 0; if(args.level) self.world.loadFromLevel(args.level, true); self.world.preloading = args.preload; @@ -456,6 +479,7 @@ self.onWorldError = function onWorldError(error) { else { console.log("Non-UserCodeError:", error.toString() + "\n" + error.stack || error.stackTrace); self.postMessage({type: 'non-user-code-problem', problem: {message: error.toString()}}); + return false; } /* We don't actually have the recoverable property any more; hmm if(!error.recoverable) { diff --git a/app/assets/main.html b/app/assets/main.html index a12b612ef..f799de987 100644 --- a/app/assets/main.html +++ b/app/assets/main.html @@ -6,10 +6,10 @@ <!--[if !IE]><!--><html lang="en"> <!--<![endif]--> <head> <meta charset="utf-8"> - <meta name="viewport" content="width=device-width,initial-scale=1"> + <meta name="viewport" content="width=1024"> <title>CodeCombat - Learn how to code by playing a game</title> - <meta name="description" content="Learn programming with a multiplayer live coding strategy game for beginners. Learn Python or JavaScript as you defeat ogres, solve mazes, and level up. Free, open source HTML5 game!"> + <meta name="description" content="Learn programming with a multiplayer live coding strategy game for beginners. Learn Python or JavaScript as you defeat ogres, solve mazes, and level up. Open source HTML5 game!"> <meta property="og:title" content="CodeCombat: Learn to Code by Playing a Game"> <meta property="og:url" content="http://codecombat.com"> @@ -22,22 +22,73 @@ <meta name="twitter:url" content="http://codecombat.com"> <meta name="twitter:site" content="CodeCombat"> <meta name="twitter:image:src" content="http://codecombat.com/images/pages/base/logo_square_250.png"> - <meta name="twitter:description" content="Learn programming with a multiplayer live coding strategy game for beginners. Learn Python or JavaScript as you defeat ogres, solve mazes, and level up. Free, open source HTML5 game!"> + <meta name="twitter:description" content="Learn programming with a multiplayer live coding strategy game for beginners. Learn Python or JavaScript as you defeat ogres, solve mazes, and level up. Open source HTML5 game!"> <link href="https://plus.google.com/115285980638641924488" rel="publisher" /> <link rel="shortcut icon" href="/images/favicon.ico"> <link rel="stylesheet" href="/stylesheets/app.css"> + <!-- Google Analytics --> + <script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + ga('create', 'UA-39724129-1', 'auto'); + </script> + + <!-- Errorception --> + <script> + (function(_,e,rr,s){_errs=[s];var c=_.onerror;_.onerror=function(){var a=arguments;_errs.push(a); + c&&c.apply(this,a)};var b=function(){var c=e.createElement(rr),b=e.getElementsByTagName(rr)[0]; + c.src="//beacon.errorception.com/"+s+".js";c.async=!0;b.parentNode.insertBefore(c,b)}; + _.addEventListener?_.addEventListener("load",b,!1):_.attachEvent("onload",b)}) + (window,document,"script","51a79585ee207206390002a2"); + </script> + + <script src="https://checkout.stripe.com/checkout.js"></script> + + <!-- IE9 doesn't support defer attribute: https://github.com/h5bp/lazyweb-requests/issues/42 --> + <!--[if IE 9]> + <script src="/lib/ace/ace.js"></script> + <script src="/javascripts/box2d.js"></script> + <script src="/javascripts/vendor.js"></script> + <script src="/javascripts/aether.js"></script> + <script src="/javascripts/app.js"></script> + <![endif]--> + <!-- IE9 cors support breaks analytics logging: http://caniuse.com/#feat=cors --> + <!--[if IE 9]> + <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery-ajaxtransport-xdomainrequest/1.0.2/jquery.xdomainrequest.min.js"></script> + <![endif]--> + <![if (gt IE 9)|(!IE)]> <script src="/lib/ace/ace.js" defer></script> - <!--[if IE 9]><script src="/javascripts/box2d.js"></script><![endif]--> <script src="/javascripts/vendor.js" defer></script> <script src="/javascripts/aether.js" defer></script> - <script src="/javascripts/app.js" defer></script> <!-- it's all Backbone! --> + <script src="/javascripts/app.js" defer></script> + <![endif]> <script> + + // Placeholder for iPad, which inspects the user object at the bottom of an injected page. + window.userObject = "userObjectTag"; + window.me = { + get: function(attribute) { return window.userObject[attribute]; } + } + onLoad = function() { - FastClick.attach(document.body); - window.userObject = "userObjectTag"; - require('core/initialize'); + try { + // IE8 warning + var htmlElement = document.querySelector("html"); + if (htmlElement) { + var classAttribute = htmlElement.getAttribute('class'); + if (classAttribute && classAttribute.indexOf('lt-ie9') >= 0) { + alert("CodeCombat does not run in Internet Explorer 8 or older. Sorry!"); + } + } + + // IE8 can't handle this + FastClick.attach(document.body); + require('core/initialize'); + } catch (error) { } } </script> @@ -50,24 +101,8 @@ <div id="modal-wrapper" class="modal-content"></div> - <div class="modal fade" id="module-loading-list"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <h4 class="modal-title">LOADING</h4> - </div> - <div class="modal-body"> - <ul class="list-group"></ul> - </div> - </div> - </div> + <div class="progress" id="module-load-progress"> + <div class="progress-bar"></div> </div> - - <!--<div class="panel panel-primary" id="module-loading-list">--> - <!--<div class="panel-heading">--> - <!--<div class="panel-title">LOADING</div>--> - <!--</div>--> - <!--<ul class="list-group"></ul>--> - <!--</div>--> </body> </html> diff --git a/app/collections/PatchesCollection.coffee b/app/collections/PatchesCollection.coffee index cb03c3486..950baac4f 100644 --- a/app/collections/PatchesCollection.coffee +++ b/app/collections/PatchesCollection.coffee @@ -6,4 +6,5 @@ module.exports = class PatchesCollection extends CocoCollection initialize: (models, options, forModel, @status='pending') -> super(arguments...) - @url = "#{forModel.urlRoot}/#{forModel.get('original')}/patches?status=#{@status}" + identifier = if not forModel.get('original') then '_id' else 'original' + @url = "#{forModel.urlRoot}/#{forModel.get(identifier)}/patches?status=#{@status}" diff --git a/app/core/ModuleLoader.coffee b/app/core/ModuleLoader.coffee index 7fe5a20e2..f11a5591d 100644 --- a/app/core/ModuleLoader.coffee +++ b/app/core/ModuleLoader.coffee @@ -9,7 +9,6 @@ module.exports = ModuleLoader = class ModuleLoader extends CocoClass @WADS = [ 'lib' 'views/play' - 'views/game-menu' 'views/editor' ] @@ -18,10 +17,20 @@ module.exports = ModuleLoader = class ModuleLoader extends CocoClass @loaded = {} @queue = new createjs.LoadQueue() @queue.on('fileload', @onFileLoad, @) + wrapped = _.wrap window.require, (func, name, loaderPath) -> + # vendor libraries aren't actually wrapped with common.js, so short circuit those requires + return {} if _.string.startsWith(name, 'vendor/') + return {} if name is 'tests' + name = 'core/auth' if name is 'lib/auth' # proxy for iPad until it's been updated to use the new, refactored location. TODO: remove this + return func(name, loaderPath) + _.extend wrapped, window.require # for functions like 'list' + window.require = wrapped + @updateProgress = _.throttle _.bind(@updateProgress, @), 700 + @lastShownProgress = 0 load: (path, first=true) -> + $('#module-load-progress').css('opacity', 1) if first - $('#module-loading-list ul').empty() @recentPaths = [] @recentLoadedBytes = 0 @@ -29,14 +38,8 @@ module.exports = ModuleLoader = class ModuleLoader extends CocoClass wad = _.find ModuleLoader.WADS, (wad) -> _.string.startsWith(path, wad) path = wad if wad return false if @loaded[path] - $('#module-loading-list').modal('show') if first @loaded[path] = true @recentPaths.push(path) - li = $("<li class='list-group-item loading' data-path='#{path}'>#{path}</li>") - .prepend($("<span class='glyphicon glyphicon-minus'></span>")) - .prepend($("<span class='glyphicon glyphicon-ok'></span>")) - ul = $('#module-loading-list ul') - ul.append(li).scrollTop(ul[0].scrollHeight) console.debug 'Loading js file:', "/javascripts/app/#{path}.js" if LOG @queue.loadFile({ id: path @@ -45,7 +48,7 @@ module.exports = ModuleLoader = class ModuleLoader extends CocoClass }) return true - loadLanguage: (langCode) -> + loadLanguage: (langCode='en-US') -> loading = @load("locale/#{langCode}") firstBit = langCode[...2] return loading if firstBit is langCode @@ -53,23 +56,45 @@ module.exports = ModuleLoader = class ModuleLoader extends CocoClass return @load("locale/#{firstBit}", false) or loading onFileLoad: (e) => - $("#module-loading-list li[data-path='#{e.item.id}']").removeClass('loading').addClass('success') - have = window.require.list() - console.group('Dependencies', e.item.id) if LOG - @recentLoadedBytes += e.rawResult.length - dependencies = @parseDependencies(e.rawResult) - console.groupEnd() if LOG - missing = _.difference dependencies, have - @load(module, false) for module in missing - locale.update() if _.string.startsWith(e.item.id, 'locale') + # load dependencies if it's not a vendor library + if not _.string.startsWith(e.item.id, 'vendor') + have = window.require.list() + console.group('Dependencies', e.item.id) if LOG + @recentLoadedBytes += e.rawResult.length + dependencies = @parseDependencies(e.rawResult) + console.groupEnd() if LOG + missing = _.difference dependencies, have + @load(module, false) for module in missing + + # update locale data + if _.string.startsWith(e.item.id, 'locale') + locale.update() + + # just a bit of cleanup to get the script objects out of the body element $(e.result).remove() + + # get treema set up only when the library loads, if it loads + if e.item.id is 'vendor/treema' + treemaExt = require 'core/treema-ext' + treemaExt.setup() + + # a module and its dependencies have loaded! if @queue.progress is 1 - $('#module-loading-list').modal('hide') @recentPaths.sort() console.debug @recentPaths.join('\n') console.debug 'loaded', @recentPaths.length, 'files,', parseInt(@recentLoadedBytes/1024), 'KB' @trigger 'load-complete' + @trigger 'loaded', e.item + + @updateProgress() + + updateProgress: -> + return if @queue.progress < @lastShownProgress + $('#module-load-progress .progress-bar').css('width', (100*@queue.progress)+'%') + if @queue.progress is 1 + $('#module-load-progress').css('opacity', 0) + parseDependencies: (raw) -> bits = raw.match(/(require\(['"](.+?)['"])|(register\(['"].+?['"])/g) or [] rootFolder = null @@ -91,6 +116,7 @@ module.exports = ModuleLoader = class ModuleLoader extends CocoClass console.groupEnd() if LOG return dependencies + # basically ripped out of commonjs definition expand: (root, name) -> results = [] if /^\.\.?(\/|$)/.test(name) diff --git a/app/core/ParticleMan.coffee b/app/core/ParticleMan.coffee new file mode 100644 index 000000000..26bdca41a --- /dev/null +++ b/app/core/ParticleMan.coffee @@ -0,0 +1,425 @@ +CocoClass = require 'core/CocoClass' +utils = require 'core/utils' + +module.exports = ParticleMan = class ParticleMan extends CocoClass + + constructor: -> + return @unsupported = true unless Modernizr.webgl + try + @renderer = new THREE.WebGLRenderer alpha: true + catch err + return @unsupported = true + $(@renderer.domElement).addClass 'particle-man' + @scene = new THREE.Scene() + @clock = new THREE.Clock() + @particleGroups = [] + + destroy: -> + @detach() + @disposeObject3D @scene + for child in @scene?.children?.slice() or [] + @scene.remove child + super() + + disposeObject3D: (obj) -> + return unless obj + @disposeObject3D child for child in obj.children + obj.geometry?.dispose() + obj.geometry = undefined + if obj.material + material.dispose() for material in obj.material.materials ? [] + obj.material.dispose() + obj.material = undefined + if obj.texture + obj.texture.dispose() + obj.texture = undefined + + attach: (@$el) -> + return if @unsupported + width = @$el.innerWidth() + height = @$el.innerHeight() + @aspectRatio = width / height + @renderer.setSize(width, height) + @$el.append @renderer.domElement + @camera = camera = new THREE.OrthographicCamera( + 100 * -0.5, # Left + 100 * 0.5, # Right + 100 * 0.5 * @aspectRatio, # Top + 100 * -0.5 * @aspectRatio, # Bottom + 0, # Near frustrum distance + 1000 # Far frustrum distance + ) + @camera.position.set(0, 0, 100) + @camera.up = new THREE.Vector3(0, 1, 0) # http://stackoverflow.com/questions/14271672/moving-the-camera-lookat-and-rotations-in-three-js + @camera.lookAt new THREE.Vector3(0, 0, 0) + unless @started + @started = true + @render() + + detach: -> + return if @unsupported + @renderer.domElement.remove() + @started = false + + render: => + return if @unsupported + return if @destroyed + return unless @started + @renderer.render @scene, @camera + dt = @clock.getDelta() + for group in @particleGroups + group.tick dt + requestAnimationFrame @render + #@countFPS() + + countFPS: -> + @framesRendered ?= 0 + ++@framesRendered + @lastFPS ?= new Date() + now = new Date() + if now - @lastFPS > 1000 + console.log @framesRendered, 'fps with', @particleGroups.length, 'particle groups.' + @framesRendered = 0 + @lastFPS = now + + addEmitter: (x, y, kind="level-dungeon-premium") -> + return if @unsupported + kind = kind.replace 'intro', 'dungeon' + options = $.extend true, {}, particleKinds[kind] + return console.error "Couldn't find particle configuration for", kind unless options.group + options.group.texture = THREE.ImageUtils.loadTexture "/images/common/particles/#{options.group.texture}.png" + scale = 100 + aspectRatio = @$el + group = new SPE.Group options.group + group.mesh.position.x = scale * (-0.5 + x) + group.mesh.position.y = scale * (-0.5 + y) * @aspectRatio + emitter = new SPE.Emitter options.emitter + group.addEmitter emitter + @particleGroups.push group + @scene.add group.mesh + group + + removeEmitter: (group) -> + return if @unsupported + @scene.remove group.mesh + @particleGroups = _.without @particleGroups, group + + removeEmitters: -> + return if @unsupported + @removeEmitter group for group in @particleGroups.slice() + + #addTestCube: -> + #geometry = new THREE.BoxGeometry 5, 5, 5 + #material = new THREE.MeshLambertMaterial color: 0xFF0000 + #mesh = new THREE.Mesh geometry, material + #@scene.add mesh + #light = new THREE.PointLight 0xFFFF00 + #light.position.set 10, 0, 20 + #@scene.add light + + +hsl = (hue, saturation, lightness) -> + new THREE.Color utils.hslToHex([hue, saturation, lightness]) +vec = (x, y, z) -> + new THREE.Vector3 x, y, z + +defaults = + group: + texture: 'star' + maxAge: 1.9 + radius: 0.75 + hasPerspective: 1 + colorize: 1 + transparent: 1 + alphaTest: 0.5 + depthWrite: false + depthTest: true + blending: THREE.NormalBlending + emitter: + type: "disk" + particleCount: 100 + radius: 1 + position: vec 0, 0, 0 + positionSpread: vec 1, 0, 1 + acceleration: vec 0, 2, 0 + accelerationSpread: vec 0, 0, 0 + velocity: vec 0, 4, 0 + velocitySpread: vec 2, 2, 2 + sizeStart: 6 + sizeStartSpread: 1 + sizeMiddle: 4 + sizeMiddleSpread: 1 + sizeEnd: 2 + sizeEndSpread: 1 + angleStart: 0 + angleStartSpread: 0 + angleMiddle: 0 + angleMiddleSpread: 0 + angleEnd: 0 + angleEndSpread: 0 + angleAlignVelocity: false + colorStart: hsl 0.55, 0.75, 0.75 + colorStartSpread: vec 0.3, 0.3, 0.3 + colorMiddle: hsl 0.55, 0.6, 0.5 + colorMiddleSpread: vec 0.2, 0.2, 0.2 + colorEnd: hsl 0.55, 0.5, 0.25 + colorEndSpread: vec 0.1, 0.1, 0.1 + opacityStart: 1 + opacityStartSpread: 0 + opacityMiddle: 0.75 + opacityMiddleSpread: 0 + opacityEnd: 0.25 + opacityEndSpread: 0 + duration: null + alive: 1 + isStatic: 0 + +ext = (d, options) -> + $.extend true, {}, d, options ? {} + +particleKinds = + 'level-dungeon-premium': ext defaults + 'level-forest-premium': ext defaults, + emitter: + colorStart: hsl 0.56, 0.97, 0.5 + colorMiddle: hsl 0.56, 0.57, 0.5 + colorEnd: hsl 0.56, 0.17, 0.5 + 'level-desert-premium': ext defaults, + emitter: + colorStart: hsl 0.56, 0.97, 0.5 + colorMiddle: hsl 0.56, 0.57, 0.5 + colorEnd: hsl 0.56, 0.17, 0.5 + 'level-mountain-premium': ext defaults, + emitter: + colorStart: hsl 0.56, 0.97, 0.5 + colorMiddle: hsl 0.56, 0.57, 0.5 + colorEnd: hsl 0.56, 0.17, 0.5 + 'level-glacier-premium': ext defaults, + emitter: + colorStart: hsl 0.56, 0.97, 0.5 + colorMiddle: hsl 0.56, 0.57, 0.5 + colorEnd: hsl 0.56, 0.17, 0.5 + 'level-volcano-premium': ext defaults, + emitter: + colorStart: hsl 0.56, 0.97, 0.5 + colorMiddle: hsl 0.56, 0.57, 0.5 + colorEnd: hsl 0.56, 0.17, 0.5 + +particleKinds['level-dungeon-premium-hero'] = ext particleKinds['level-dungeon-premium'], + emitter: + particleCount: 200 + radius: 1.5 + acceleration: vec 0, 4, 0 + opacityStart: 0.25 + opacityMiddle: 0.5 + opacityEnd: 0.75 + +particleKinds['level-dungeon-gate'] = ext particleKinds['level-dungeon-premium'], + emitter: + particleCount: 2000 + acceleration: vec 0, 8, 0 + colorStart: hsl 0.5, 0.75, 0.9 + colorMiddle: hsl 0.5, 0.75, 0.7 + colorEnd: hsl 0.5, 0.75, 0.3 + colorStartSpread: vec 1, 1, 1 + colorMiddleSpread: vec 1.5, 1.5, 1.5 + colorEndSpread: vec 2.5, 2.5, 2.5 + +particleKinds['level-dungeon-hero-ladder'] = particleKinds['level-dungeon-course-ladder'] = ext particleKinds['level-dungeon-premium'], + emitter: + particleCount: 200 + acceleration: vec 0, 3, 0 + colorStart: hsl 0, 0.75, 0.7 + colorMiddle: hsl 0, 0.75, 0.5 + colorEnd: hsl 0, 0.75, 0.3 + +particleKinds['level-dungeon-replayable'] = particleKinds['level-dungeon-replayable-premium'] = ext particleKinds['level-dungeon-hero-ladder'], + emitter: + colorStart: hsl 0.17, 0.75, 0.7 + colorMiddle: hsl 0.17, 0.75, 0.5 + colorEnd: hsl 0.17, 0.75, 0.3 + +particleKinds['level-forest-premium-hero'] = ext particleKinds['level-forest-premium'], + emitter: + particleCount: 200 + radius: 1.5 + acceleration: vec 0, 4, 0 + opacityStart: 0.25 + opacityMiddle: 0.5 + opacityEnd: 0.75 + +particleKinds['level-forest-gate'] = ext particleKinds['level-forest-premium'], + emitter: + particleCount: 120 + velocity: vec 0, 8, 0 + colorStart: hsl 0.56, 0.97, 0.3 + colorMiddle: hsl 0.56, 0.57, 0.3 + colorEnd: hsl 0.56, 0.17, 0.3 + colorStartSpread: vec 1, 1, 1 + colorMiddleSpread: vec 1.5, 1.5, 1.5 + colorEndSpread: vec 2.5, 2.5, 2.5 + +particleKinds['level-forest-hero-ladder'] = particleKinds['level-forest-course-ladder'] = ext particleKinds['level-forest-premium'], + emitter: + particleCount: 90 + velocity: vec 0, 4, 0 + colorStart: hsl 0, 0.95, 0.3 + colorMiddle: hsl 0, 1, 0.5 + colorEnd: hsl 0, 0.75, 0.1 + +particleKinds['level-forest-replayable'] = particleKinds['level-forest-replayable-premium'] = ext particleKinds['level-forest-hero-ladder'], + emitter: + colorStart: hsl 0.17, 0.75, 0.7 + colorMiddle: hsl 0.17, 0.75, 0.5 + colorEnd: hsl 0.17, 0.75, 0.3 + +particleKinds['level-forest-premium-item'] = ext particleKinds['level-forest-gate'], + emitter: + particleCount: 2000 + radius: 2.5 + acceleration: vec 0, 8, 1 + opacityStart: 0 + opacityMiddle: 0.5 + opacityEnd: 0.75 + colorStart: hsl 0.5, 0.75, 0.9 + colorMiddle: hsl 0.5, 0.75, 0.7 + colorEnd: hsl 0.5, 0.75, 0.3 + colorStartSpread: vec 1, 1, 1 + colorMiddleSpread: vec 1.5, 1.5, 1.5 + colorEndSpread: vec 2.5, 2.5, 2.5 + +particleKinds['level-desert-premium-hero'] = ext particleKinds['level-desert-premium'], + emitter: + particleCount: 200 + radius: 1.5 + acceleration: vec 0, 4, 0 + opacityStart: 0.25 + opacityMiddle: 0.5 + opacityEnd: 0.75 + +particleKinds['level-desert-gate'] = ext particleKinds['level-desert-premium'], + emitter: + particleCount: 120 + velocity: vec 0, 8, 0 + colorStart: hsl 0.56, 0.97, 0.3 + colorMiddle: hsl 0.56, 0.57, 0.3 + colorEnd: hsl 0.56, 0.17, 0.3 + colorStartSpread: vec 1, 1, 1 + colorMiddleSpread: vec 1.5, 1.5, 1.5 + colorEndSpread: vec 2.5, 2.5, 2.5 + +particleKinds['level-desert-hero-ladder'] = particleKinds['level-desert-course-ladder'] = ext particleKinds['level-desert-premium'], + emitter: + particleCount: 90 + velocity: vec 0, 4, 0 + colorStart: hsl 0, 0.95, 0.3 + colorMiddle: hsl 0, 1, 0.5 + colorEnd: hsl 0, 0.75, 0.1 + +particleKinds['level-desert-replayable'] = particleKinds['level-desert-replayable-premium'] = ext particleKinds['level-desert-hero-ladder'], + emitter: + colorStart: hsl 0.17, 0.75, 0.7 + colorMiddle: hsl 0.17, 0.75, 0.5 + colorEnd: hsl 0.17, 0.75, 0.3 + +particleKinds['level-mountain-premium-hero'] = ext particleKinds['level-mountain-premium'], + emitter: + particleCount: 200 + radius: 1.5 + acceleration: vec 0, 4, 0 + opacityStart: 0.25 + opacityMiddle: 0.5 + opacityEnd: 0.75 + +particleKinds['level-mountain-gate'] = ext particleKinds['level-mountain-premium'], + emitter: + particleCount: 120 + velocity: vec 0, 8, 0 + colorStart: hsl 0.56, 0.97, 0.3 + colorMiddle: hsl 0.56, 0.57, 0.3 + colorEnd: hsl 0.56, 0.17, 0.3 + colorStartSpread: vec 1, 1, 1 + colorMiddleSpread: vec 1.5, 1.5, 1.5 + colorEndSpread: vec 2.5, 2.5, 2.5 + +particleKinds['level-mountain-hero-ladder'] = particleKinds['level-mountain-course-ladder'] = ext particleKinds['level-mountain-premium'], + emitter: + particleCount: 90 + velocity: vec 0, 4, 0 + colorStart: hsl 0, 0.95, 0.3 + colorMiddle: hsl 0, 1, 0.5 + colorEnd: hsl 0, 0.75, 0.1 + +particleKinds['level-mountain-replayable'] = particleKinds['level-mountain-replayable-premium'] = ext particleKinds['level-mountain-hero-ladder'], + emitter: + colorStart: hsl 0.17, 0.75, 0.7 + colorMiddle: hsl 0.17, 0.75, 0.5 + colorEnd: hsl 0.17, 0.75, 0.3 + +particleKinds['level-glacier-premium-hero'] = ext particleKinds['level-glacier-premium'], + emitter: + particleCount: 200 + radius: 1.5 + acceleration: vec 0, 4, 0 + opacityStart: 0.25 + opacityMiddle: 0.5 + opacityEnd: 0.75 + +particleKinds['level-glacier-gate'] = ext particleKinds['level-glacier-premium'], + emitter: + particleCount: 120 + velocity: vec 0, 8, 0 + colorStart: hsl 0.56, 0.97, 0.3 + colorMiddle: hsl 0.56, 0.57, 0.3 + colorEnd: hsl 0.56, 0.17, 0.3 + colorStartSpread: vec 1, 1, 1 + colorMiddleSpread: vec 1.5, 1.5, 1.5 + colorEndSpread: vec 2.5, 2.5, 2.5 + +particleKinds['level-glacier-hero-ladder'] = particleKinds['level-glacier-course-ladder'] = ext particleKinds['level-glacier-premium'], + emitter: + particleCount: 90 + velocity: vec 0, 4, 0 + colorStart: hsl 0, 0.95, 0.3 + colorMiddle: hsl 0, 1, 0.5 + colorEnd: hsl 0, 0.75, 0.1 + +particleKinds['level-glacier-replayable'] = particleKinds['level-glacier-replayable-premium'] = ext particleKinds['level-glacier-hero-ladder'], + emitter: + colorStart: hsl 0.17, 0.75, 0.7 + colorMiddle: hsl 0.17, 0.75, 0.5 + colorEnd: hsl 0.17, 0.75, 0.3 + +particleKinds['level-volcano-premium-hero'] = ext particleKinds['level-volcano-premium'], + emitter: + particleCount: 200 + radius: 1.5 + acceleration: vec 0, 4, 0 + opacityStart: 0.25 + opacityMiddle: 0.5 + opacityEnd: 0.75 + +particleKinds['level-volcano-gate'] = ext particleKinds['level-volcano-premium'], + emitter: + particleCount: 120 + velocity: vec 0, 8, 0 + colorStart: hsl 0.56, 0.97, 0.3 + colorMiddle: hsl 0.56, 0.57, 0.3 + colorEnd: hsl 0.56, 0.17, 0.3 + colorStartSpread: vec 1, 1, 1 + colorMiddleSpread: vec 1.5, 1.5, 1.5 + colorEndSpread: vec 2.5, 2.5, 2.5 + +particleKinds['level-volcano-hero-ladder'] = ext particleKinds['level-volcano-premium'], + emitter: + particleCount: 90 + velocity: vec 0, 4, 0 + colorStart: hsl 0, 0.95, 0.3 + colorMiddle: hsl 0, 1, 0.5 + colorEnd: hsl 0, 0.75, 0.1 + +particleKinds['level-volcano-replayable'] = particleKinds['level-volcano-replayable-premium'] = ext particleKinds['level-volcano-hero-ladder'], + emitter: + colorStart: hsl 0.17, 0.75, 0.7 + colorMiddle: hsl 0.17, 0.75, 0.5 + colorEnd: hsl 0.17, 0.75, 0.3 diff --git a/app/core/Router.coffee b/app/core/Router.coffee index cf0c8dd74..263140bea 100644 --- a/app/core/Router.coffee +++ b/app/core/Router.coffee @@ -1,8 +1,6 @@ gplusClientID = '800329290710-j9sivplv2gpcdgkrsis9rff3o417mlfa.apps.googleusercontent.com' # TODO: Move to GPlusHandler -NotFoundView = require('views/core/NotFoundView') - go = (path) -> -> @routeDirectly path, arguments module.exports = class CocoRouter extends Backbone.Router @@ -22,23 +20,33 @@ module.exports = class CocoRouter extends Backbone.Router 'account': go('account/MainAccountView') 'account/settings': go('account/AccountSettingsRootView') 'account/unsubscribe': go('account/UnsubscribeView') - 'account/profile': go('user/JobProfileView') # legacy URL, sent in emails + #'account/profile': go('user/JobProfileView') # legacy URL, sent in emails + 'account/profile': go('EmployersView') # Show the not-recruiting-now screen 'account/payments': go('account/PaymentsView') + 'account/subscription': go('account/SubscriptionView') + 'account/invoices': go('account/InvoicesView') 'admin': go('admin/MainAdminView') 'admin/candidates': go('admin/CandidatesView') 'admin/clas': go('admin/CLAsView') 'admin/employers': go('admin/EmployersListView') 'admin/files': go('admin/FilesView') - 'admin/growth': go('admin/GrowthView') + 'admin/analytics/users': go('admin/AnalyticsUsersView') + 'admin/analytics/subscriptions': go('admin/AnalyticsSubscriptionsView') 'admin/level-sessions': go('admin/LevelSessionsView') 'admin/users': go('admin/UsersView') 'admin/base': go('admin/BaseView') + 'admin/trial-requests': go('admin/TrialRequestsView') 'admin/user-code-problems': go('admin/UserCodeProblemsView') + 'admin/pending-patches': go('admin/PendingPatchesView') 'beta': go('HomeView') 'cla': go('CLAView') + + 'clans': go('clans/ClansView') + 'clans/:clanID': go('clans/ClanDetailsView') + 'community': go('CommunityView') 'contribute': go('contribute/MainContributeView') @@ -49,6 +57,12 @@ module.exports = class CocoRouter extends Backbone.Router 'contribute/diplomat': go('contribute/DiplomatView') 'contribute/scribe': go('contribute/ScribeView') + 'courses': go('courses/mock1/CoursesView') + 'courses/mock1': go('courses/mock1/CoursesView') + 'courses/mock1/enroll': go('courses/mock1/CourseEnrollView') + 'courses/mock1/:courseID': go('courses/mock1/CourseDetailsView') + 'courses/mock1/:courseID/info': go('courses/mock1/CourseInfoView') + 'db/*path': 'routeToServer' 'demo(/*subpath)': go('DemoView') 'docs/components': go('docs/ComponentsDocumentationView') @@ -65,6 +79,9 @@ module.exports = class CocoRouter extends Backbone.Router 'editor/level/:levelID': go('editor/level/LevelEditView') 'editor/thang': go('editor/thang/ThangTypeSearchView') 'editor/thang/:thangID': go('editor/thang/ThangTypeEditView') + 'editor/campaign/:campaignID': go('editor/campaign/CampaignEditorView') + 'editor/poll': go('editor/poll/PollSearchView') + 'editor/poll/:articleID': go('editor/poll/PollEditView') 'employers': go('EmployersView') @@ -77,35 +94,40 @@ module.exports = class CocoRouter extends Backbone.Router 'i18n/component/:handle': go('i18n/I18NEditComponentView') 'i18n/level/:handle': go('i18n/I18NEditLevelView') 'i18n/achievement/:handle': go('i18n/I18NEditAchievementView') + 'i18n/campaign/:handle': go('i18n/I18NEditCampaignView') + 'i18n/poll/:handle': go('i18n/I18NEditPollView') + + 'identify': go('user/IdentifyView') 'legal': go('LegalView') 'multiplayer': go('MultiplayerView') - 'play-old': go('play/MainPlayView') # This used to be 'play'. - 'play': go('play/WorldMapView') - 'play/ladder/:levelID': go('play/ladder/LadderView') - 'play/ladder': go('play/ladder/MainLadderView') + 'play': go('play/CampaignView') + 'play/ladder/:levelID': go('ladder/LadderView') + 'play/ladder': go('ladder/MainLadderView') 'play/level/:levelID': go('play/level/PlayLevelView') 'play/spectate/:levelID': go('play/SpectateView') - 'play/:map': go('play/WorldMapView') + 'play/:map': go('play/CampaignView') 'preview': go('HomeView') 'teachers': go('TeachersView') + 'teachers/freetrial': go('TeachersFreeTrialView') 'test(/*subpath)': go('TestView') 'user/:slugOrID': go('user/MainUserView') - 'user/:slugOrID/profile': go('user/JobProfileView') + #'user/:slugOrID/profile': go('user/JobProfileView') + 'user/:slugOrID/profile': go('EmployersView') # Show the not-recruiting-now screen - '*name': 'showNotFoundView' + '*name': go('NotFoundView') routeToServer: (e) -> window.location.href = window.location.href routeDirectly: (path, args) -> - path = "views/#{path}" if not _.str.startsWith(path, 'views/') + path = "views/#{path}" if not _.string.startsWith(path, 'views/') ViewClass = @tryToLoadModule path if not ViewClass and application.moduleLoader.load(path) @listenToOnce application.moduleLoader, 'load-complete', -> @@ -123,13 +145,6 @@ module.exports = class CocoRouter extends Backbone.Router if error.toString().search('Cannot find module "' + path + '" from') is -1 throw error - showNotFoundView: -> - @openView @notFoundView() - - notFoundView: -> - view = new NotFoundView() - view.render() - openView: (view) -> @closeCurrentView() $('#page-container').empty().append view.el @@ -169,7 +184,7 @@ module.exports = class CocoRouter extends Backbone.Router callback: 'signinCallback', clientid: gplusClientID, cookiepolicy: 'single_host_origin', - scope: 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email', + scope: 'https://www.googleapis.com/auth/plus.login email', height: 'short', } if gapi.signin?.render @@ -185,10 +200,19 @@ module.exports = class CocoRouter extends Backbone.Router window.tracker?.trackPageView() onNavigate: (e) -> + if _.isString e.viewClass + ViewClass = @tryToLoadModule e.viewClass + if not ViewClass and application.moduleLoader.load(e.viewClass) + @listenToOnce application.moduleLoader, 'load-complete', -> + @onNavigate(e) + return + e.viewClass = ViewClass + manualView = e.view or e.viewClass if (e.route is document.location.pathname) and not manualView return document.location.reload() @navigate e.route, {trigger: not manualView} + @_trackPageView() return unless manualView if e.viewClass args = e.viewArgs or [] diff --git a/app/core/Tracker.coffee b/app/core/Tracker.coffee index 144aab299..7766ef69d 100644 --- a/app/core/Tracker.coffee +++ b/app/core/Tracker.coffee @@ -1,6 +1,9 @@ {me} = require 'core/auth' +SuperModel = require 'models/SuperModel' +utils = require 'core/utils' debugAnalytics = false +targetInspectJSLevelSlugs = ['cupboards-of-kithgard'] module.exports = class Tracker constructor: -> @@ -8,49 +11,140 @@ module.exports = class Tracker console.error 'Overwrote our Tracker!', window.tracker window.tracker = @ @isProduction = document.location.href.search('codecombat.com') isnt -1 + @trackReferrers() @identify() + @supermodel = new SuperModel() - identify: (traits) -> - console.log 'Would identify', traits if debugAnalytics - return unless me and @isProduction and analytics? and not me.isAdmin() - # https://segment.io/docs/methods/identify - traits ?= {} - for userTrait in ['email', 'anonymous', 'dateCreated', 'name', 'wizardColor1', 'testGroupNumber', 'gender'] + enableInspectletJS: (levelSlug) -> + # InspectletJS loading is delayed and targeting specific levels for more focused investigations + return @disableInspectletJS() unless levelSlug in targetInspectJSLevelSlugs + + # Start embed code + window.__insp = window.__insp or [] + __insp.push [ + 'wid' + 2102699786 + ] + do -> + __ldinsp = -> + insp = document.createElement('script') + insp.type = 'text/javascript' + insp.async = true + insp.id = 'inspsync' + insp.src = (if 'https:' == document.location.protocol then 'https' else 'http') + '://cdn.inspectlet.com/inspectlet.js' + x = document.getElementsByTagName('script')[0] + x.parentNode.insertBefore insp, x + return + + if window.attachEvent + window.attachEvent 'onload', __ldinsp + else + window.addEventListener 'load', __ldinsp, false + # End embed code + + # Identify and track pageview here, because inspectlet is loaded too late for standard Tracker calls + @identify() + # http://www.inspectlet.com/docs#virtual_pageviews + __insp?.push ['virtualPage'] + + disableInspectletJS: -> + delete window.__insp + + trackReferrers: -> + elapsed = new Date() - new Date(me.get('dateCreated')) + return unless elapsed < 5 * 60 * 1000 + return if me.get('siteref') or me.get('referrer') + changed = false + if siteref = utils.getQueryVariable '_r' + me.set 'siteref', siteref + changed = true + if referrer = document.referrer + me.set 'referrer', referrer + changed = true + me.patch() if changed + + identify: (traits={}) -> + return unless me + + # Save explicit traits for internal tracking + @explicitTraits ?= {} + @explicitTraits[key] = value for key, value of traits + + for userTrait in ['email', 'anonymous', 'dateCreated', 'name', 'testGroupNumber', 'gender', 'lastLevel', 'siteref', 'ageRange'] traits[userTrait] ?= me.get(userTrait) - analytics.identify me.id, traits + console.log 'Would identify', traits if debugAnalytics + return unless @isProduction and not me.isAdmin() + + # Errorception + # https://errorception.com/docs/meta + _errs?.meta = traits + + # Inspectlet + # https://www.inspectlet.com/docs#identifying_users + __insp?.push ['identify', me.id] + __insp?.push ['tagSession', traits] trackPageView: -> - return unless @isProduction and analytics? and not me.isAdmin() - url = Backbone.history.getFragment() - console.log 'Going to track visit for', "/#{url}" if debugAnalytics - analytics.pageview "/#{url}" + name = Backbone.history.getFragment() + console.log "Would track analytics pageview: '/#{name}'" if debugAnalytics + return unless @isProduction and not me.isAdmin() - trackEvent: (action, properties, includeProviders=null) => - # 'action' is a string - # Google Analytics properties format: {category: 'Account', label: 'Premium', value: 50 } - # https://segment.com/docs/integrations/google-analytics/#track - # https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#Anatomy - # Mixpanel properties format: whatever you want unlike GA - # https://segment.com/docs/integrations/mixpanel/ - console.log 'Would track analytics event:', action, properties if debugAnalytics - return unless me and @isProduction and analytics? and not me.isAdmin() - console.log 'Going to track analytics event:', action, properties if debugAnalytics - properties = properties or {} - context = {} + # Google Analytics + # https://developers.google.com/analytics/devguides/collection/analyticsjs/pages + ga? 'send', 'pageview', "/#{name}" - # TODO: Restrict providers, if given includeProviders - # TODO: This method may not work anymore, because it is not referenced in the segment.io docs - # TODO: Can double check in Mixpanel - # TODO: https://segment.com/docs/api/tracking/track/ - if includeProviders - context.providers = {'All': false} - for provider in includeProviders - context.providers[provider] = true + trackEvent: (action, properties={}) => + @trackEventInternal action, _.cloneDeep properties unless me?.isAdmin() and @isProduction + console.log 'Tracking external analytics event:', action, properties if debugAnalytics + return unless me and @isProduction and not me.isAdmin() - analytics?.track action, properties, context + # Google Analytics + # https://developers.google.com/analytics/devguides/collection/analyticsjs/events + gaFieldObject = + hitType: 'event' + eventCategory: properties.category ? 'All' + eventAction: action + gaFieldObject.eventLabel = properties.label if properties.label? + gaFieldObject.eventValue = properties.value if properties.value? + ga? 'send', gaFieldObject - trackTiming: (duration, category, variable, label, samplePercentage=5) -> - # https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingTiming + # Inspectlet + # http://www.inspectlet.com/docs#tagging + __insp?.push ['tagSession', action: action, properies: properties] + + trackEventInternal: (event, properties) => + # Skipping heavily logged actions we don't use internally + unless event in ['Simulator Result', 'Started Level Load', 'Finished Level Load'] + # Trimming properties we don't use internally + # TODO: delete properites.level for 'Saw Victory' after 2/8/15. Should be using levelID instead. + if event in ['Clicked Start Level', 'Inventory Play', 'Heard Sprite', 'Started Level', 'Saw Victory', 'Click Play', 'Choose Inventory', 'Homepage Loaded', 'Change Hero'] + delete properties.category + delete properties.label + else if event in ['Loaded World Map', 'Started Signup', 'Finished Signup', 'Login', 'Facebook Login', 'Google Login', 'Show subscription modal'] + delete properties.category + + properties[key] = value for key, value of @explicitTraits if @explicitTraits? + console.log 'Tracking internal analytics event:', event, properties if debugAnalytics + if @isProduction + eventObject = {} + eventObject["event"] = event + eventObject["properties"] = properties unless _.isEmpty properties + eventObject["user"] = me.id + dataToSend = JSON.stringify eventObject + # console.log dataToSend if debugAnalytics + $.post("#{window.location.protocol or 'http:'}//analytics.codecombat.com/analytics", dataToSend).fail -> + console.error "Analytics post failed!" + else + request = @supermodel.addRequestResource 'log_event', { + url: '/db/analytics_log_event/-/log_event' + data: {event: event, properties: properties} + method: 'POST' + }, 0 + request.load() + + trackTiming: (duration, category, variable, label) -> + # https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings return console.warn "Duration #{duration} invalid for trackTiming call." unless duration >= 0 and duration < 60 * 60 * 1000 console.log 'Would track timing event:', arguments if debugAnalytics - window._gaq?.push ['_trackTiming', category, variable, duration, label, samplePercentage] + return unless me and @isProduction and not me.isAdmin() + ga? 'send', 'timing', category, variable, duration, label diff --git a/app/core/application.coffee b/app/core/application.coffee index 91bc5cc46..dc212dec5 100644 --- a/app/core/application.coffee +++ b/app/core/application.coffee @@ -5,6 +5,7 @@ ModuleLoader = require 'core/ModuleLoader' locale = require 'locale/locale' {me} = require 'core/auth' Tracker = require 'core/Tracker' +CocoModel = require 'models/CocoModel' marked.setOptions {gfm: true, sanitize: true, smartLists: true, breaks: false} @@ -29,14 +30,22 @@ elementAcceptsKeystrokes = (el) -> # not radio, checkbox, range, or color return (tag is 'textarea' or (tag is 'input' and type in textInputTypes) or el.contentEditable in ['', 'true']) and not (el.readOnly or el.disabled) -COMMON_FILES = ['/images/pages/base/modal_background.png', '/images/level/code_palette_wood_background.png', '/images/level/popover_background.png', '/images/level/code_editor_background.png'] +COMMON_FILES = ['/images/pages/base/modal_background.png', '/images/level/popover_background.png', '/images/level/code_palette_wood_background.png', '/images/level/code_editor_background_border.png'] preload = (arrayOfImages) -> $(arrayOfImages).each -> $('<img/>')[0].src = @ +# IE9 doesn't expose console object unless debugger tools are loaded +window.console ?= + info: -> + log: -> + error: -> + debug: -> +console.debug ?= console.log # Needed for IE10 and earlier + Application = initialize: -> Router = require('core/Router') - @isProduction = -> document.location.href.search('codecombat.com') isnt -1 + @isProduction = -> document.location.href.search('https?://localhost:3000') is -1 @isIPadApp = webkit?.messageHandlers? and navigator.userAgent?.indexOf('iPad') isnt -1 $('body').addClass 'ipad' if @isIPadApp @tracker = new Tracker() @@ -47,6 +56,7 @@ Application = initialize: -> @moduleLoader.loadLanguage(me.get('preferredLanguage', true)) $(document).bind 'keydown', preventBackspace preload(COMMON_FILES) + CocoModel.pollAchievements() $.i18n.init { lng: me.get('preferredLanguage', true) fallbackLng: 'en' diff --git a/app/core/auth.coffee b/app/core/auth.coffee index 0fc6044cd..75e321de0 100644 --- a/app/core/auth.coffee +++ b/app/core/auth.coffee @@ -37,20 +37,25 @@ module.exports.createUserWithoutReload = (userObject, failure=backboneFailure) - Backbone.Mediator.publish('created-user-without-reload') }) -module.exports.loginUser = (userObject, failure=genericFailure) -> +module.exports.loginUser = (userObject, failure=genericFailure, nextURL=null) -> console.log 'logging in as', userObject.email jqxhr = $.post('/auth/login', { username: userObject.email, password: userObject.password }, - (model) -> window.location.reload() + (model) -> if nextURL then window.location.href = nextURL else window.location.reload() ) jqxhr.fail(failure) module.exports.logoutUser = -> FB?.logout?() - res = $.post('/auth/logout', {}, -> window.location.reload()) + callback = -> + if (window.location.href.indexOf("/account/settings") > -1) + window.location = '/' + else + window.location.reload() + res = $.post('/auth/logout', {}, callback) res.fail(genericFailure) onSetVolume = (e) -> diff --git a/app/core/contact.coffee b/app/core/contact.coffee index 4e1422b59..2db72bc2e 100644 --- a/app/core/contact.coffee +++ b/app/core/contact.coffee @@ -1,6 +1,7 @@ module.exports.sendContactMessage = (contactMessageObject, modal) -> - modal.find('.sending-indicator').show() + modal?.find('.sending-indicator').show() jqxhr = $.post '/contact', contactMessageObject, (response) -> + return unless modal modal.find('.sending-indicator').hide() modal.find('#contact-message').val('Thanks!') _.delay -> diff --git a/app/core/deltas.coffee b/app/core/deltas.coffee index 07e6b03da..3b81c69ed 100644 --- a/app/core/deltas.coffee +++ b/app/core/deltas.coffee @@ -118,7 +118,7 @@ module.exports.getConflicts = (headDeltas, pendingDeltas) -> offset += 1 # these stop being substrings of each other? Then conflict DNE - if not (nextPath.startsWith path) then break + if not (_.string.startsWith nextPath, path) then break # check if these two are from the same group, but we still need to check for more beyond unless headPathMap[path] or headPathMap[nextPath] then continue diff --git a/app/core/errors.coffee b/app/core/errors.coffee index 13b429ba8..bfdd36409 100644 --- a/app/core/errors.coffee +++ b/app/core/errors.coffee @@ -1,4 +1,4 @@ -errorModalTemplate = require 'templates/modal/error' +errorModalTemplate = require 'templates/core/error' {applyErrorsToForm} = require 'core/forms' module.exports.parseServerError = (text) -> @@ -42,7 +42,7 @@ module.exports.connectionFailure = connectionFailure = -> showErrorModal(html) showErrorModal = (html) -> - # TODO: make a views/modal/error_modal view for this to use so the template can reuse templates/modal/modal_base? + # TODO: make a views/modal/error_modal view for this to use so the template can reuse templates/core/modal-base? $('#modal-wrapper').html(html) $('.modal:visible').modal('hide') $('#modal-error').modal('show') diff --git a/app/core/initialize.coffee b/app/core/initialize.coffee index f3ad262e8..b29291612 100644 --- a/app/core/initialize.coffee +++ b/app/core/initialize.coffee @@ -1,5 +1,5 @@ Backbone.Mediator.setValidationEnabled false -app = require 'core/application' +app = null channelSchemas = 'auth': require 'schemas/subscriptions/auth' @@ -21,20 +21,27 @@ definitionSchemas = 'misc': require 'schemas/definitions/misc' init = -> + return if app + if not window.userObject._id + $.ajax '/auth/whoami', cache: false, success: (res) -> + window.userObject = res + init() + return + + app = require 'core/application' setupConsoleLogging() watchForErrors() setUpIOSLogging() path = document.location.pathname - app.testing = path.startsWith '/test' - app.demoing = path.startsWith '/demo' - initializeUtilityServices() unless app.testing or app.demoing + app.testing = _.string.startsWith path, '/test' + app.demoing = _.string.startsWith path, '/demo' setUpBackboneMediator() app.initialize() Backbone.history.start({ pushState: true }) handleNormalUrls() setUpMoment() # Set up i18n for moment - treemaExt = require 'core/treema-ext' - treemaExt.setup() + +module.exports.init = init handleNormalUrls = -> # http://artsy.github.com/blog/2012/06/25/replacing-hashbang-routes-with-pushstate/ @@ -75,10 +82,14 @@ setUpMoment = -> me.on 'change:preferredLanguage', (me) -> moment.lang me.get('preferredLanguage', true), {} -initializeUtilityServices = -> - require('core/services/segmentio')() - setupConsoleLogging = -> + # IE9 doesn't expose console object unless debugger tools are loaded + unless console? + window.console = + info: -> + log: -> + error: -> + debug: -> unless console.debug # Needed for IE10 and earlier console.debug = console.log diff --git a/app/core/services/google.coffee b/app/core/services/google.coffee index bda5d1d24..2b51765ee 100644 --- a/app/core/services/google.coffee +++ b/app/core/services/google.coffee @@ -9,7 +9,7 @@ module.exports = initializeGoogle = -> po = document.createElement('script') po.type = 'text/javascript' po.async = true - po.src = 'https://apis.google.com/js/client:plusone.js?onload=onGPlusLoaded' + po.src = 'https://apis.google.com/js/client:platform.js?onload=onGPlusLoaded' s = document.getElementsByTagName('script')[0] s.parentNode.insertBefore po, s return diff --git a/app/core/services/segmentio.coffee b/app/core/services/segmentio.coffee deleted file mode 100644 index 565959690..000000000 --- a/app/core/services/segmentio.coffee +++ /dev/null @@ -1,40 +0,0 @@ -module.exports = initializeSegmentio = -> - analytics = analytics or [] - (-> - e = [ - 'identify' - 'track' - 'trackLink' - 'trackForm' - 'trackClick' - 'trackSubmit' - 'page' - 'pageview' - 'ab' - 'alias' - 'ready' - 'group' - ] - t = (e) -> - -> - analytics.push [e].concat(Array::slice.call(arguments, 0)) - return - - n = 0 - - while n < e.length - analytics[e[n]] = t(e[n]) - n++ - return - )() - analytics.load = (e) -> - t = document.createElement('script') - t.type = 'text/javascript' - t.async = not 0 - t.src = ((if 'https:' is document.location.protocol then 'https://' else 'http://')) + 'd2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/' + e + '/analytics.min.js' - - n = document.getElementsByTagName('script')[0] - n.parentNode.insertBefore t, n - return - - analytics.load 'jsjzx9n4d2' diff --git a/app/core/services/stripe.coffee b/app/core/services/stripe.coffee index 0a457b362..785ba194e 100644 --- a/app/core/services/stripe.coffee +++ b/app/core/services/stripe.coffee @@ -1,10 +1,11 @@ publishableKey = if application.isProduction() then 'pk_live_27jQZozjDGN1HSUTnSuM578g' else 'pk_test_zG5UwVu6Ww8YhtE9ZYh0JO6a' - + module.exports = handler = StripeCheckout.configure({ key: publishableKey name: 'CodeCombat' email: me.get('email') - image: '/images/pages/base/logo_square_250.png' + image: "https://codecombat.com/images/pages/base/logo_square_250.png" token: (token) -> Backbone.Mediator.publish 'stripe:received-token', { token: token } -}) \ No newline at end of file + locale: 'auto' +}) diff --git a/app/core/social-handlers/FacebookHandler.coffee b/app/core/social-handlers/FacebookHandler.coffee index 84c6323e7..cc88f98b3 100644 --- a/app/core/social-handlers/FacebookHandler.coffee +++ b/app/core/social-handlers/FacebookHandler.coffee @@ -42,12 +42,15 @@ module.exports = FacebookHandler = class FacebookHandler extends CocoClass me.set('facebookID', r.id) if r.id Backbone.Mediator.publish 'auth:logging-in-with-facebook', {} - window.tracker?.trackEvent 'Facebook Login' window.tracker?.identify() + beforeID = me.id me.patch({ error: backboneFailure, url: "/db/user/#{me.id}?facebookID=#{r.id}&facebookAccessToken=#{@authResponse.accessToken}" success: (model) -> + window.tracker?.trackEvent 'Facebook Login', category: "Signup", label: 'Facebook' + if model.id is beforeID + window.tracker?.trackEvent 'Finished Signup', category: "Signup", label: 'Facebook' window.location.reload() if model.get('email') isnt oldEmail }) diff --git a/app/core/social-handlers/GPlusHandler.coffee b/app/core/social-handlers/GPlusHandler.coffee index e60ba5303..ca40485d0 100644 --- a/app/core/social-handlers/GPlusHandler.coffee +++ b/app/core/social-handlers/GPlusHandler.coffee @@ -15,11 +15,11 @@ fieldsToFetch = 'displayName,gender,image,name(familyName,givenName),id' plusURL = '/plus/v1/people/me?fields='+fieldsToFetch revokeUrl = 'https://accounts.google.com/o/oauth2/revoke?token=' clientID = '800329290710-j9sivplv2gpcdgkrsis9rff3o417mlfa.apps.googleusercontent.com' -scope = 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email' +scope = 'https://www.googleapis.com/auth/plus.login email' module.exports = GPlusHandler = class GPlusHandler extends CocoClass constructor: -> - @accessToken = storage.load GPLUS_TOKEN_KEY + @accessToken = storage.load GPLUS_TOKEN_KEY, false super() subscriptions: @@ -52,57 +52,43 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass @loggedIn = true try # Without removing this, we sometimes get a cross-domain error - d = JSON.stringify(_.omit(e, 'g-oauth-window')) - storage.save(GPLUS_TOKEN_KEY, d) + d = _.omit(e, 'g-oauth-window') + storage.save(GPLUS_TOKEN_KEY, d, 0) catch e console.error 'Unable to save G+ token key', e @accessToken = e @trigger 'logged-in' - + loginCodeCombat: -> # email and profile data loaded separately - gapi.client.request(path: plusURL, callback: @onPersonEntityReceived) - gapi.client.load('oauth2', 'v2', => - gapi.client.oauth2.userinfo.get().execute(@onEmailReceived)) + gapi.client.load('plus', 'v1', => + gapi.client.plus.people.get({userId: 'me'}).execute(@onPersonReceived)) - shouldSave: false - - onPersonEntityReceived: (r) => + onPersonReceived: (r) => for gpProp, userProp of userPropsToSave keys = gpProp.split('.') value = r - value = value[key] for key in keys + for key in keys + value = value[key] if value and not me.get(userProp) - @shouldSave = true me.set(userProp, value) - @responsesComplete += 1 - @personLoaded = true - @trigger 'person-loaded' - @saveIfAllDone() - - onEmailReceived: (r) => - newEmail = r.email and r.email isnt me.get('email') + newEmail = r.emails?.length and r.emails[0] isnt me.get('email') return unless newEmail or me.get('anonymous', true) - me.set('email', r.email) - @shouldSave = true - @emailLoaded = true - @trigger 'email-loaded' - @saveIfAllDone() + me.set('email', r.emails[0].value) + @trigger 'person-loaded' + @save() - saveIfAllDone: => - console.debug 'Save if all done. Person loaded:', @personLoaded, 'and email loaded:', @emailLoaded - return unless @personLoaded and @emailLoaded + save: => console.debug 'Email, gplusID:', me.get('email'), me.get('gplusID') return unless me.get('email') and me.get('gplusID') Backbone.Mediator.publish 'auth:logging-in-with-gplus', {} gplusID = me.get('gplusID') - window.tracker?.trackEvent 'Google Login' window.tracker?.identify() patch = {} patch[key] = me.get(key) for gplusKey, key of userPropsToSave - patch._id = me.id + patch._id = beforeID = me.id patch.email = me.get('email') wasAnonymous = me.get('anonymous') @trigger 'logging-into-codecombat' @@ -111,11 +97,14 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass patch: true type: 'PUT' error: -> - console.debug('Logging into GPlus fail.', arguments) + console.warn('Logging into GPlus fail.', arguments) backboneFailure(arguments...) url: "/db/user?gplusID=#{gplusID}&gplusAccessToken=#{@accessToken.access_token}" success: (model) -> - console.debug('GPLus login success!') + console.info('GPLus login success!') + window.tracker?.trackEvent 'Google Login', category: "Signup" + if model.id is beforeID + window.tracker?.trackEvent 'Finished Signup', label: 'GPlus' window.location.reload() if wasAnonymous and not model.get('anonymous') }) diff --git a/app/core/storage.coffee b/app/core/storage.coffee index 21e8d59a9..f31e8b300 100644 --- a/app/core/storage.coffee +++ b/app/core/storage.coffee @@ -1,4 +1,6 @@ -module.exports.load = (key) -> +# Pass false for fromCache to fetch keys that have been stored outside of lscache. +module.exports.load = (key, fromCache=true) -> + return lscache.get key if fromCache s = localStorage.getItem(key) return null unless s try @@ -8,8 +10,17 @@ module.exports.load = (key) -> console.warn('error loading from storage', key) return null -module.exports.save = (key, value) -> - s = JSON.stringify(value) - localStorage.setItem(key, s) +# Pass 0 for expirationInMinutes to persist it as long as possible outside of lscache expiration. +module.exports.save = (key, value, expirationInMinutes) -> + expirationInMinutes ?= 7 * 24 * 60 + if expirationInMinutes + lscache.set key, value, expirationInMinutes + else + localStorage.setItem key, JSON.stringify(value) -module.exports.remove = (key) -> localStorage.removeItem key +# Pass false for fromCache to remove keys that have been stored outside of lscache. +module.exports.remove = (key, fromCache=true) -> + if fromCache + lscache.remove key + else + localStorage.removeItem key diff --git a/app/core/treema-ext.coffee b/app/core/treema-ext.coffee index c29c087a9..189e2de0f 100644 --- a/app/core/treema-ext.coffee +++ b/app/core/treema-ext.coffee @@ -34,7 +34,7 @@ class LiveEditingMarkup extends TreemaNode.nodeMap.ace valEl.append($('<div class="preview"></div>').hide()) addImageUpload: (valEl) -> - return unless me.isAdmin() + return unless me.isAdmin() or me.isArtisan() valEl.append( $('<div class="pick-image-button"></div>').append( $('<button>Pick Image</button>') @@ -96,8 +96,16 @@ class SoundFileTreema extends TreemaNode.nodeMap.string buildValueForDisplay: (valEl, data) -> mimetype = "audio/#{@keyForParent}" + mimetypes = [mimetype] + if mimetype is 'audio/mp3' + # https://github.com/codecombat/codecombat/issues/445 + # http://stackoverflow.com/questions/10688588/which-mime-type-should-i-use-for-mp3 + mimetypes.push 'audio/mpeg' + else if mimetype is 'audio/ogg' + mimetypes.push 'application/ogg' + mimetypes.push 'video/ogg' # huh, that's what it took to be able to upload ogg sounds in Firefox pickButton = $('<a class="btn btn-primary btn-xs"><span class="glyphicon glyphicon-upload"></span></a>') - .click(=> filepicker.pick {mimetypes:[mimetype]}, @onFileChosen) + .click(=> filepicker.pick {mimetypes: mimetypes}, @onFileChosen) playButton = $('<a class="btn btn-primary btn-xs"><span class="glyphicon glyphicon-play"></span></a>') .click(@playFile) stopButton = $('<a class="btn btn-primary btn-xs"><span class="glyphicon glyphicon-stop"></span></a>') @@ -116,7 +124,7 @@ class SoundFileTreema extends TreemaNode.nodeMap.string menu = $('<div class="dropdown-menu"></div>') files = @getFiles() for file in files - continue unless file.get('contentType') is mimetype + continue unless file.get('contentType') in mimetypes path = file.get('metadata').path filename = file.get 'filename' fullPath = [path, filename].join('/') @@ -313,7 +321,7 @@ class InternationalizationNode extends TreemaNode.nodeMap.object class LatestVersionCollection extends CocoCollection -class LatestVersionReferenceNode extends TreemaNode +module.exports.LatestVersionReferenceNode = class LatestVersionReferenceNode extends TreemaNode searchValueTemplate: '<input placeholder="Search" /><div class="treema-search-results"></div>' valueClass: 'treema-latest-version' url: '/db/article' @@ -383,7 +391,11 @@ class LatestVersionReferenceNode extends TreemaNode m = CocoModel.getReferencedModel(@getData(), @workingSchema) data = @getData() if _.isString data # LatestVersionOriginalReferenceNode just uses original - m = @settings.supermodel.getModelByOriginal(m.constructor, data) + if m.schema().properties.version + m = @settings.supermodel.getModelByOriginal(m.constructor, data) + else + # get by id + m = @settings.supermodel.getModel(m.constructor, data) else m = @settings.supermodel.getModelByOriginalAndMajorVersion(m.constructor, data.original, data.majorVersion) if @instance and not m @@ -434,7 +446,7 @@ class LatestVersionReferenceNode extends TreemaNode selected = @getSelectedResultEl() return not selected.length -class LatestVersionOriginalReferenceNode extends LatestVersionReferenceNode +module.exports.LatestVersionOriginalReferenceNode = class LatestVersionOriginalReferenceNode extends LatestVersionReferenceNode # Just for saving the original, not the major version. saveChanges: -> selected = @getSelectedResultEl() @@ -443,6 +455,15 @@ class LatestVersionOriginalReferenceNode extends LatestVersionReferenceNode @data = fullValue.attributes.original @instance = fullValue +module.exports.IDReferenceNode = class IDReferenceNode extends LatestVersionReferenceNode + # Just for saving the _id + saveChanges: -> + selected = @getSelectedResultEl() + return unless selected.length + fullValue = selected.data('value') + @data = fullValue.attributes._id + @instance = fullValue + class LevelComponentReferenceNode extends LatestVersionReferenceNode # HACK: this list of properties is needed by the thang components edit view and config views. # need a better way to specify this, or keep the search models from bleeding into those @@ -459,6 +480,44 @@ class SlugPropsObject extends TreemaNode.nodeMap.object return res if @workingSchema.properties?[res]? _.string.slugify(res) +class TaskTreema extends TreemaNode.nodeMap.string + buildValueForDisplay: (valEl) -> + @taskCheckbox = $('<input type="checkbox">').prop 'checked', @data.complete + task = $("<span>#{@data.name}</span>") + valEl.append(@taskCheckbox).append(task) + @taskCheckbox.on 'change', @onTaskChanged + + buildValueForEditing: (valEl, data) -> + @nameInput = @buildValueForEditingSimply(valEl, data.name) + @nameInput.parent().prepend(@taskCheckbox) + + onTaskChanged: (e) => + @markAsChanged() + @saveChanges() + @flushChanges() + @broadcastChanges() + + onEditInputBlur: (e) => + @markAsChanged() + @saveChanges() + if @isValid() then @display() if @isEditing() else @nameInput.focus().select() + @flushChanges() + @broadcastChanges() + + saveChanges: (oldData) -> + @data ?= {} + @data.name = @nameInput.val() if @nameInput + @data.complete = Boolean(@taskCheckbox.prop 'checked') + + destroy: -> + @taskCheckbox.off() + super() + + +#class CheckboxTreema extends TreemaNode.nodeMap.boolean +# TODO: try this out + + module.exports.setup = -> TreemaNode.setNodeSubclass('date-time', DateTimeTreema) TreemaNode.setNodeSubclass('version', VersionTreema) @@ -475,3 +534,5 @@ module.exports.setup = -> TreemaNode.setNodeSubclass('i18n', InternationalizationNode) TreemaNode.setNodeSubclass('sound-file', SoundFileTreema) TreemaNode.setNodeSubclass 'slug-props', SlugPropsObject + TreemaNode.setNodeSubclass 'task', TaskTreema + #TreemaNode.setNodeSubclass 'checkbox', CheckboxTreema diff --git a/app/core/utils.coffee b/app/core/utils.coffee index 5a4e78594..f5a846e59 100644 --- a/app/core/utils.coffee +++ b/app/core/utils.coffee @@ -97,10 +97,15 @@ createQuadraticFunc = (params) -> createLogFunc = (params) -> (x) -> if x > 0 then (params.a or 1) * Math.log((params.b or 1) * (x + (params.c or 0))) + (params.d or 0) else 0 +# f(x) = ax^b + c +createPowFunc = (params) -> + (x) -> (params.a or 1) * Math.pow(x, params.b or 1) + (params.c or 0) + module.exports.functionCreators = linear: positify(createLinearFunc) quadratic: positify(createQuadraticFunc) logarithmic: positify(createLogFunc) + pow: positify(createPowFunc) # Call done with true to satisfy the 'until' goal and stop repeating func module.exports.keepDoingUntil = (func, wait=100, totalWait=5000) -> @@ -130,3 +135,66 @@ module.exports.kindaEqual = compare = (l, r) -> return true else return false + +# Return UTC string "YYYYMMDD" for today + offset +module.exports.getUTCDay = (offset=0) -> + day = new Date() + day.setDate(day.getUTCDate() + offset) + partYear = day.getUTCFullYear() + partMonth = (day.getUTCMonth() + 1) + partMonth = "0" + partMonth if partMonth < 10 + partDay = day.getUTCDate() + partDay = "0" + partDay if partDay < 10 + "#{partYear}#{partMonth}#{partDay}" + +# Fast, basic way to replace text in an element when you don't need much. +# http://stackoverflow.com/a/4962398/540620 +if document?.createElement + dummy = document.createElement 'div' + dummy.innerHTML = 'text' + TEXT = if dummy.textContent is 'text' then 'textContent' else 'innerText' + module.exports.replaceText = (elems, text) -> + elem[TEXT] = text for elem in elems + null + +# Add a stylesheet rule +# http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript/26230472#26230472 +# Don't use wantonly, or we'll have to implement a simple mechanism for clearing out old rules. +if document?.createElement + module.exports.injectCSS = ((doc) -> + # wrapper for all injected styles and temp el to create them + wrap = doc.createElement("div") + temp = doc.createElement("div") + # rules like "a {color: red}" etc. + return (cssRules) -> + # append wrapper to the body on the first call + unless wrap.id + wrap.id = "injected-css" + wrap.style.display = "none" + doc.body.appendChild wrap + # <br> for IE: http://goo.gl/vLY4x7 + temp.innerHTML = "<br><style>" + cssRules + "</style>" + wrap.appendChild temp.children[1] + return + )(document) + +module.exports.getQueryVariable = getQueryVariable = (param, defaultValue) -> + query = document.location.search.substring 1 + pairs = (pair.split('=') for pair in query.split '&') + for pair in pairs when pair[0] is param + return {'true': true, 'false': false}[pair[1]] ? decodeURIComponent(pair[1]) + defaultValue + +module.exports.getSponsoredSubsAmount = getSponsoredSubsAmount = (price=999, subCount=0, personalSub=false) -> + # 1 100% + # 2-11 80% + # 12+ 60% + # TODO: make this less confusing + return 0 unless subCount > 0 + offset = if personalSub then 1 else 0 + if subCount <= 1 - offset + price + else if subCount <= 11 - offset + Math.round((1 - offset) * price + (subCount - 1 + offset) * price * 0.8) + else + Math.round((1 - offset) * price + 10 * price * 0.8 + (subCount - 11 + offset) * price * 0.6) diff --git a/app/lib/Angel.coffee b/app/lib/Angel.coffee index 8aec202f2..eaa0d762c 100644 --- a/app/lib/Angel.coffee +++ b/app/lib/Angel.coffee @@ -5,6 +5,9 @@ World = require 'lib/world/world' CocoClass = require 'core/CocoClass' GoalManager = require 'lib/world/GoalManager' +{sendContactMessage} = require 'core/contact' + +reportedLoadErrorAlready = false module.exports = class Angel extends CocoClass @nicks: ['Archer', 'Lana', 'Cyril', 'Pam', 'Cheryl', 'Woodhouse', 'Ray', 'Krieger'] @@ -16,16 +19,21 @@ module.exports = class Angel extends CocoClass subscriptions: 'level:flag-updated': 'onFlagEvent' 'playback:stop-real-time-playback': 'onStopRealTimePlayback' + 'level:escape-pressed': 'onEscapePressed' constructor: (@shared) -> super() @say 'Got my wings.' - if window.navigator and (window.navigator.userAgent.search('MSIE') isnt -1 or window.navigator.appName is 'Microsoft Internet Explorer') - @infiniteLoopIntervalDuration *= 10 # since it's so slow to serialize without transferable objects, we can't trust it + isIE = window.navigator and (window.navigator.userAgent.search('MSIE') isnt -1 or window.navigator.appName is 'Microsoft Internet Explorer') + if isIE or @shared.headless + # Since IE is so slow to serialize without transferable objects, we can't trust it. + # We also noticed the headless_client simulator needing more time. (This does both Simulators, though.) + @infiniteLoopIntervalDuration *= 10 @infiniteLoopTimeoutDuration *= 10 @abortTimeoutDuration *= 10 @initialized = false @running = false + @allLogs = [] @hireWorker() @shared.angels.push @ @@ -39,12 +47,13 @@ module.exports = class Angel extends CocoClass # say: debugging stuff, usually off; log: important performance indicators, keep on say: (args...) -> #@log args... - log: -> - # console.info.apply is undefined in IE9, CofeeScript splats invocation won't work. + log: -> + # console.info.apply is undefined in IE9, CoffeeScript splats invocation won't work. # http://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function message = "|#{@shared.godNick}'s #{@nick}|" message += " #{arg}" for arg in arguments console.info message + @allLogs.push message testWorker: => return if @destroyed @@ -82,7 +91,7 @@ module.exports = class Angel extends CocoClass when 'non-user-code-problem' Backbone.Mediator.publish 'god:non-user-code-problem', problem: event.data.problem if @shared.firstWorld - @infinitelyLooped() # For now, this should do roughly the right thing if it happens during load. + @infinitelyLooped(false, true) # For now, this should do roughly the right thing if it happens during load. else @fireWorker() @@ -162,14 +171,28 @@ module.exports = class Angel extends CocoClass @worker.postMessage func: 'finalizePreload' @work.preload = false - infinitelyLooped: => + infinitelyLooped: (escaped=false, nonUserCodeProblem=false) => @say 'On infinitely looped! Aborting?', @aborting return if @aborting problem = type: 'runtime', level: 'error', id: 'runtime_InfiniteLoop', message: 'Code never finished. It\'s either really slow or has an infinite loop.' + problem.message = 'Escape pressed; code aborted.' if escaped Backbone.Mediator.publish 'god:user-code-problem', problem: problem - Backbone.Mediator.publish 'god:infinite-loop', firstWorld: @shared.firstWorld + Backbone.Mediator.publish 'god:infinite-loop', firstWorld: @shared.firstWorld, nonUserCodeProblem: nonUserCodeProblem + @reportLoadError() if nonUserCodeProblem @fireWorker() + reportLoadError: -> + return if me.isAdmin() or /dev=true/.test(window.location?.href ? '') or reportedLoadErrorAlready + reportedLoadErrorAlready = true + context = email: me.get('email') + context.message = "Automatic Report - Unable to Load Level\nLogs:\n" + @allLogs.join('\n') + if $.browser + context.browser = "#{$.browser.platform} #{$.browser.name} #{$.browser.versionNumber}" + context.screenSize = "#{screen?.width ? $(window).width()} x #{screen?.height ? $(window).height()}" + context.subject = "Level Load Error: #{@work?.level?.name or 'Unknown Level'}" + context.levelSlug = @work?.level?.slug + sendContactMessage context + doWork: -> return if @aborting return @say 'Not initialized for work yet.' unless @initialized @@ -236,8 +259,14 @@ module.exports = class Angel extends CocoClass onStopRealTimePlayback: (e) -> return unless @running and @work.realTime @work.realTime = false + @lastRealTimeWork = new Date() @worker.postMessage func: 'stopRealTimePlayback' + onEscapePressed: (e) -> + return unless @running and not @work.realTime + return if (new Date() - @lastRealTimeWork) < 1000 # Fires right after onStopRealTimePlayback + @infinitelyLooped true + #### Synchronous code for running worlds on main thread (profiling / IE9) #### simulateSync: (work) => console?.profile? "World Generation #{(Math.random() * 1000).toFixed(0)}" if imitateIE9? @@ -246,6 +275,7 @@ module.exports = class Angel extends CocoClass work.testWorld.levelSessionIDs = work.levelSessionIDs work.testWorld.submissionCount = work.submissionCount work.testWorld.flagHistory = work.flagHistory ? [] + work.testWorld.difficulty = work.difficulty testWorld.loadFromLevel work.level work.testWorld.preloading = work.preload work.testWorld.headless = work.headless diff --git a/app/lib/AudioPlayer.coffee b/app/lib/AudioPlayer.coffee index 020b00dd5..9d7143192 100644 --- a/app/lib/AudioPlayer.coffee +++ b/app/lib/AudioPlayer.coffee @@ -76,6 +76,14 @@ class AudioPlayer extends CocoClass return sound if sound = say[message] if _.string.startsWith message, 'attack' return sound if sound = say.attack + if message.indexOf("i-dont-see-anyone") isnt -1 + return sound if sound = say['i-dont-see-anyone'] + if message.indexOf("i-see-you") isnt -1 + return sound if sound = say['i-see-you'] + if message.indexOf("repeating-loop") isnt -1 + return sound if sound = say['repeating-loop'] + if /move(up|down|left|right)/.test message + return sound if sound = say["move-#{message[4...]}"] defaults = say.defaultSimlish if say.swearingSimlish?.length and _.find(swears, (s) -> message.search(s) isnt -1) defaults = say.swearingSimlish @@ -103,9 +111,6 @@ class AudioPlayer extends CocoClass @soundsToPlayWhenLoaded[name] = audioOptions.volume audioOptions = @applyPanning audioOptions, pos if @camera and not @camera.destroyed and pos instance = createjs.Sound.play name, audioOptions - # For some reason, individual sound volume control doesn't work any more. - # I tried updating to SoundJS NEXT on 2014-09-10, but couldn't get any sounds to play with that one. - #console.log 'got instance with volume', instance.volume, instance._volume, instance.gainNode?.gain.value instance hasLoadedSound: (filename, name) -> diff --git a/app/lib/CampaignOptions.coffee b/app/lib/CampaignOptions.coffee deleted file mode 100644 index 8fb64858a..000000000 --- a/app/lib/CampaignOptions.coffee +++ /dev/null @@ -1,25 +0,0 @@ -CampaignList = require('views/play/WorldMapView').campaigns - -# TODO: Is this file structured correctly? - -# Per-campaign options, with default fallback set -options = - 'default': - autocompleteFontSizePx: 16 - backspaceThrottle: false - 'dungeon': - autocompleteFontSizePx: 20 - backspaceThrottle: true - -module.exports = CampaignOptions = - getCampaignForSlug: (slug) -> - return unless slug - for campaign in CampaignList - for level in campaign.levels - return campaign.id if level.id is slug - - getOption: (levelSlug, option) -> - return unless levelSlug and option - return unless campaign = CampaignOptions.getCampaignForSlug levelSlug - return options[campaign]?[option] if options[campaign]?[option]? - return options.default[option] if options.default[option]? diff --git a/app/lib/DefaultScripts.coffee b/app/lib/DefaultScripts.coffee index 818feee91..2c2fd804d 100644 --- a/app/lib/DefaultScripts.coffee +++ b/app/lib/DefaultScripts.coffee @@ -8,7 +8,7 @@ module.exports = [ focus: bounds: [{x: 0, y: 0}, {x: 80, y: 68}] target: "Hero Placeholder" - zoom: 2 + zoom: 0.5 sound: music: file: "/music/music_level_2" diff --git a/app/lib/God.coffee b/app/lib/God.coffee index cdaabdcd7..b22f6dcd7 100644 --- a/app/lib/God.coffee +++ b/app/lib/God.coffee @@ -63,6 +63,7 @@ module.exports = class God extends CocoClass onTomeCast: (e) -> @lastSubmissionCount = e.submissionCount @lastFlagHistory = (flag for flag in e.flagHistory when flag.source isnt 'code') + @lastDifficulty = e.difficulty @createWorld e.spells, e.preload, e.realTime createWorld: (spells, preload, realTime) -> @@ -92,6 +93,7 @@ module.exports = class God extends CocoClass levelSessionIDs: @levelSessionIDs submissionCount: @lastSubmissionCount flagHistory: @lastFlagHistory + difficulty: @lastDifficulty goals: @angelsShare.goalManager?.getGoals() headless: @angelsShare.headless preload: preload @@ -123,6 +125,7 @@ module.exports = class God extends CocoClass levelSessionIDs: @levelSessionIDs submissionCount: @lastSubmissionCount flagHistory: @lastFlagHistory + difficulty: @lastDifficulty goals: @goalManager?.getGoals() frame: args.frame currentThangID: args.thangID diff --git a/app/lib/LevelBus.coffee b/app/lib/LevelBus.coffee index 6c551668f..a3bbb577a 100644 --- a/app/lib/LevelBus.coffee +++ b/app/lib/LevelBus.coffee @@ -10,16 +10,12 @@ module.exports = class LevelBus extends Bus return Bus.getFromCache(docName) or new LevelBus docName subscriptions: - 'self-wizard:target-changed': 'onSelfWizardTargetChanged' - 'self-wizard:created': 'onSelfWizardCreated' 'tome:editing-began': 'onEditingBegan' 'tome:editing-ended': 'onEditingEnded' 'script:state-changed': 'onScriptStateChanged' 'script:ended': 'onScriptEnded' 'script:reset': 'onScriptReset' - 'surface:frame-changed': 'onFrameChanged' 'surface:sprite-selected': 'onSpriteSelected' - 'level:set-playing': 'onSetPlaying' 'level:show-victory': 'onVictory' 'tome:spell-changed': 'onSpellChanged' 'tome:spell-created': 'onSpellCreated' @@ -31,7 +27,10 @@ module.exports = class LevelBus extends Bus constructor: -> super(arguments...) @changedSessionProperties = {} - @saveSession = _.debounce(@reallySaveSession, 1000, {maxWait: 5000}) + if application.isProduction() + @saveSession = _.debounce(@reallySaveSession, 4000, {maxWait: 10000}) # Save slower on production. + else + @saveSession = _.debounce(@reallySaveSession, 1000, {maxWait: 5000}) # Save quickly in development. @playerIsIdle = false init: -> @@ -54,27 +53,13 @@ module.exports = class LevelBus extends Bus return true unless @session?.get('multiplayer') super() - onSelfWizardCreated: (e) -> - @selfWizardLank = e.sprite - - onSelfWizardTargetChanged: (e) -> - @wizardRef?.child('targetPos').set(@selfWizardLank?.targetPos or null) - @wizardRef?.child('targetSprite').set(@selfWizardLank?.targetSprite?.thang.id or null) - onMeSynced: => super() - @wizardRef?.child('wizardColor1').set(me.get('wizardColor1') or 0.0) join: -> super() - @wizardRef = @myConnection.child('wizard') - @wizardRef?.child('targetPos').set(@selfWizardLank?.targetPos or null) - @wizardRef?.child('targetSprite').set(@selfWizardLank?.targetSprite?.thang.id or null) - @wizardRef?.child('wizardColor1').set(me.get('wizardColor1') or 0.0) disconnect: -> - @wizardRef?.off() - @wizardRef = null @fireScriptsRef?.off() @fireScriptsRef = null super() @@ -87,8 +72,8 @@ module.exports = class LevelBus extends Bus # UPDATING FIREBASE AND SESSION - onEditingBegan: -> @wizardRef?.child('editing').set(true) - onEditingEnded: -> @wizardRef?.child('editing').set(false) + onEditingBegan: -> #@wizardRef?.child('editing').set(true) # no more wizards + onEditingEnded: -> #@wizardRef?.child('editing').set(false) # no more wizards # HACK: Backbone does not work with nested documents, but we want to # patch only those props that have changed. Look into plugins to @@ -149,7 +134,7 @@ module.exports = class LevelBus extends Bus return unless @onPoint() @fireScriptsRef?.update(e) state = @session.get('state') - scripts = state.scripts + scripts = state.scripts ? {} scripts.currentScript = e.currentScript scripts.currentScriptOffset = e.currentScriptOffset @changedSessionProperties.state = true @@ -179,14 +164,6 @@ module.exports = class LevelBus extends Bus @changedSessionProperties.state = true @saveSession() - onFrameChanged: (e) -> - return unless @onPoint() - state = @session.get('state') - state.frame = e.frame - @session.set('state', state) - @changedSessionProperties.state = true - @saveSession() - onSpriteSelected: (e) -> return unless @onPoint() state = @session.get('state') @@ -195,14 +172,6 @@ module.exports = class LevelBus extends Bus @changedSessionProperties.state = true @saveSession() - onSetPlaying: (e) -> - return unless @onPoint() - state = @session.get('state') - state.playing = e.playing - @session.set('state', state) - @changedSessionProperties.state = true - @saveSession() - onVictory: -> return unless @onPoint() state = @session.get('state') diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index 92c31e165..11bf10eba 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -31,7 +31,9 @@ module.exports = class LevelLoader extends CocoClass @opponentSessionID = options.opponentSessionID @team = options.team @headless = options.headless + @sessionless = options.sessionless @spectateMode = options.spectateMode ? false + @observing = options.observing @worldNecessities = [] @listenTo @supermodel, 'resource-loaded', @onWorldNecessityLoaded @@ -54,12 +56,15 @@ module.exports = class LevelLoader extends CocoClass @listenToOnce @level, 'sync', @onLevelLoaded onLevelLoaded: -> - @loadSession() + @loadSession() unless @sessionless @populateLevel() # Session Loading loadSession: -> + if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] + @sessionDependenciesRegistered = {} + if @sessionID url = "/db/level.session/#{@sessionID}" else @@ -67,10 +72,12 @@ module.exports = class LevelLoader extends CocoClass url += "?team=#{@team}" if @team session = new LevelSession().setURL url + session.project = ['creator', 'team', 'heroConfig', 'codeLanguage', 'submittedCodeLanguage', 'state'] if @headless @sessionResource = @supermodel.loadModel(session, 'level_session', {cache: false}) @session = @sessionResource.model if @opponentSessionID opponentSession = new LevelSession().setURL "/db/level.session/#{@opponentSessionID}" + opponentSession.project = session.project if @headless @opponentSessionResource = @supermodel.loadModel(opponentSession, 'opponent_session', {cache: false}) @opponentSession = @opponentSessionResource.model @@ -88,7 +95,11 @@ module.exports = class LevelLoader extends CocoClass @listenToOnce @opponentSession, 'sync', @loadDependenciesForSession loadDependenciesForSession: (session) -> + if me.id isnt session.get 'creator' + session.patch = session.save = -> console.error "Not saving session, since we didn't create it." + @loadCodeLanguagesForSession session if session is @session + @addSessionBrowserInfo session # hero-ladder games require the correct session team in level:loaded team = @team ? @session.get('team') Backbone.Mediator.publish 'level:loaded', level: @level, team: team @@ -97,7 +108,6 @@ module.exports = class LevelLoader extends CocoClass else if session is @opponentSession @consolidateFlagHistory() if @session.loaded return unless @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] - @sessionDependenciesRegistered ?= {} heroConfig = session.get('heroConfig') heroConfig ?= me.get('heroConfig') if session is @session and not @headless heroConfig ?= {} @@ -124,6 +134,30 @@ module.exports = class LevelLoader extends CocoClass if _.size(@sessionDependenciesRegistered) is 2 and @checkAllWorldNecessitiesRegisteredAndLoaded() @onWorldNecessitiesLoaded() + loadCodeLanguagesForSession: (session) -> + codeLanguages = _.uniq _.filter [session.get('codeLanguage') or 'python', session.get('submittedCodeLanguage')] + for codeLanguage in codeLanguages + do (codeLanguage) => + modulePath = "vendor/aether-#{codeLanguage}" + return unless application.moduleLoader?.load modulePath + languageModuleResource = @supermodel.addSomethingResource 'language_module' + onModuleLoaded = (e) -> + return unless e.id is modulePath + languageModuleResource.markLoaded() + @stopListening application.moduleLoader, 'loaded', onModuleLoaded # listenToOnce might work here instead, haven't tried + @listenTo application.moduleLoader, 'loaded', onModuleLoaded + + addSessionBrowserInfo: (session) -> + return unless me.id is session.get 'creator' + return unless $.browser? + browser = {} + browser['desktop'] = $.browser.desktop if $.browser.desktop + browser['name'] = $.browser.name if $.browser.name + browser['platform'] = $.browser.platform if $.browser.platform + browser['version'] = $.browser.version if $.browser.version + session.set 'browser', browser + session.patch() + consolidateFlagHistory: -> state = @session.get('state') ? {} myFlagHistory = _.filter state.flagHistory ? [], team: @session.get('team') @@ -176,14 +210,10 @@ module.exports = class LevelLoader extends CocoClass for obj in objUniq articleVersions url = "/db/article/#{obj.original}/version/#{obj.majorVersion}" @maybeLoadURL url, Article, 'article' - if obj = @level.get 'nextLevel' + if obj = @level.get 'nextLevel' # TODO: update to get next level from campaigns, not this old property url = "/db/level/#{obj.original}/version/#{obj.majorVersion}" @maybeLoadURL url, Level, 'level' - unless @headless or @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] - wizard = ThangType.loadUniversalWizard() - @supermodel.loadModel wizard, 'thang' - @worldNecessities = @worldNecessities.concat worldNecessities loadThangsRequiredByLevelThang: (levelThang) -> @@ -200,7 +230,10 @@ module.exports = class LevelLoader extends CocoClass requiredThangTypes.push itemThangType for itemThangType in _.values (component.config.inventory ? {}) else if component.config.requiredThangTypes requiredThangTypes = requiredThangTypes.concat component.config.requiredThangTypes - for thangType in requiredThangTypes + extantRequiredThangTypes = _.filter requiredThangTypes + if extantRequiredThangTypes.length < requiredThangTypes.length + console.error "Some Thang had a blank required ThangType in components list:", components + for thangType in extantRequiredThangTypes url = "/db/thang.type/#{thangType}/version?project=name,components,original,rasterIcon,kind" @worldNecessities.push @maybeLoadURL(url, ThangType, 'thang') @@ -231,8 +264,8 @@ module.exports = class LevelLoader extends CocoClass checkAllWorldNecessitiesRegisteredAndLoaded: -> return false unless _.filter(@worldNecessities).length is 0 return false unless @thangNamesLoaded - return false if @sessionDependenciesRegistered and not @sessionDependenciesRegistered[@session.id] - return false if @sessionDependenciesRegistered and @opponentSession and not @sessionDependenciesRegistered[@opponentSession.id] + return false if @sessionDependenciesRegistered and not @sessionDependenciesRegistered[@session.id] and not @sessionless + return false if @sessionDependenciesRegistered and @opponentSession and not @sessionDependenciesRegistered[@opponentSession.id] and not @sessionless true onWorldNecessitiesLoaded: -> @@ -293,7 +326,7 @@ module.exports = class LevelLoader extends CocoClass resource.markLoaded() if resource.spriteSheetKeys.length is 0 denormalizeSession: -> - return if @headless or @sessionDenormalized or @spectateMode + return if @headless or @sessionDenormalized or @spectateMode or @sessionless patch = 'levelName': @level.get('name') 'levelID': @level.get('slug') or @level.id @@ -332,7 +365,7 @@ module.exports = class LevelLoader extends CocoClass @grabTeamConfigs() @thangTypeTeams = {} for thang in @level.get('thangs') - if @level.get('type', true) is 'hero' and thang.id is 'Hero Placeholder' + if @level.get('type', true) in ['hero', 'course'] and thang.id is 'Hero Placeholder' continue # No team colors for heroes on single-player levels for component in thang.components if team = component.config?.team @@ -364,6 +397,9 @@ module.exports = class LevelLoader extends CocoClass @world.levelSessionIDs = if @opponentSessionID then [@sessionID, @opponentSessionID] else [@sessionID] @world.submissionCount = @session?.get('state')?.submissionCount ? 0 @world.flagHistory = @session?.get('state')?.flagHistory ? [] + @world.difficulty = @session?.get('state')?.difficulty ? 0 + if @observing + @world.difficulty = Math.max 0, @world.difficulty - 1 # Show the difficulty they won, not the next one. serializedLevel = @level.serialize(@supermodel, @session, @opponentSession) @world.loadFromLevel serializedLevel, false console.log 'World has been initialized from level loader.' diff --git a/app/lib/LevelOptions.coffee b/app/lib/LevelOptions.coffee deleted file mode 100644 index 6e16e06d7..000000000 --- a/app/lib/LevelOptions.coffee +++ /dev/null @@ -1,293 +0,0 @@ -module.exports = LevelOptions = - 'dungeons-of-kithgard': - disableSpaces: true - hidesSubmitUntilRun: true - hidesPlayButton: true - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots'} - restrictedGear: {feet: 'leather-boots'} - requiredCode: ['moveRight'] - 'gems-in-the-deep': - disableSpaces: true - hidesSubmitUntilRun: true - hidesPlayButton: true - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots'} - restrictedGear: {feet: 'leather-boots'} - 'shadow-guard': - disableSpaces: true - hidesSubmitUntilRun: true - hidesPlayButton: true - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots'} - restrictedGear: {feet: 'leather-boots', 'right-hand': 'simple-sword'} - 'kounter-kithwise': - disableSpaces: true - hidesPlayButton: true - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots'} - restrictedGear: {feet: 'leather-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'} - 'crawlways-of-kithgard': - hidesPlayButton: true - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots'} - restrictedGear: {feet: 'leather-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'} - 'forgetful-gemsmith': - disableSpaces: true - hidesPlayButton: true - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots'} - restrictedGear: {feet: 'leather-boots', 'programming-book': 'programmaticon-i'} - 'true-names': - disableSpaces: true - hidesPlayButton: true - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', waist: 'leather-belt'} - restrictedGear: {feet: 'leather-boots', 'programming-book': 'programmaticon-i'} - requiredCode: ['Brak'] - 'favorable-odds': - disableSpaces: true - hidesPlayButton: true - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword'} - restrictedGear: {feet: 'leather-boots', 'programming-book': 'programmaticon-i'} - 'the-raised-sword': - disableSpaces: true - hidesPlayButton: true - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'tarnished-bronze-breastplate'} - restrictedGear: {feet: 'leather-boots', 'programming-book': 'programmaticon-i'} - 'the-first-kithmaze': - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} - restrictedGear: {feet: 'leather-boots'} - requiredCode: ['loop'] - 'haunted-kithmaze': - hidesRunShortcut: true - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - moveRightLoopSnippet: true - requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} - restrictedGear: {feet: 'leather-boots'} - requiredCode: ['loop'] - 'descending-further': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} - restrictedGear: {feet: 'leather-boots'} - 'the-second-kithmaze': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - moveRightLoopSnippet: true - requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} - restrictedGear: {feet: 'leather-boots'} - 'dread-door': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'} - restrictedGear: {feet: 'leather-boots'} - 'known-enemy': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', torso: 'tarnished-bronze-breastplate'} - restrictedGear: {feet: 'leather-boots'} - suspectCode: [{name: 'enemy-in-quotes', pattern: /['"]enemy/m}] # ' - 'master-of-names': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'tarnished-bronze-breastplate'} - restrictedGear: {feet: 'leather-boots'} - requiredCode: ['findNearestEnemy'] - suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] - 'lowly-kithmen': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'tarnished-bronze-breastplate'} - restrictedGear: {feet: 'leather-boots'} - requiredCode: ['findNearestEnemy'] - suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] - 'closing-the-distance': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'tarnished-bronze-breastplate', eyes: 'crude-glasses'} - restrictedGear: {feet: 'leather-boots'} - suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] - 'tactical-strike': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'tarnished-bronze-breastplate', eyes: 'crude-glasses'} - restrictedGear: {feet: 'leather-boots'} - suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] - 'the-final-kithmaze': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'tarnished-bronze-breastplate', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'} - suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] - 'the-gauntlet': - hidesHUD: true - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'tarnished-bronze-breastplate', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'} - restrictedGear: {feet: 'leather-boots', 'right-hand': 'crude-builders-hammer'} - suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] - 'kithgard-gates': - hidesSay: true - hidesCodeToolbar: true - hidesRealTimePlayback: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'crude-builders-hammer', torso: 'tarnished-bronze-breastplate'} - restrictedGear: {'right-hand': 'simple-sword'} - 'defense-of-plainswood': - hidesRealTimePlayback: true - hidesCodeToolbar: true - requiredGear: {feet: 'simple-boots', 'right-hand': 'crude-builders-hammer'} - restrictedGear: {'right-hand': 'simple-sword'} - 'winding-trail': - hidesRealTimePlayback: true - hidesCodeToolbar: true - requiredGear: {feet: 'leather-boots', 'right-hand': 'crude-builders-hammer'} - restrictedGear: {feet: 'simple-boots', 'right-hand': 'simple-sword'} - 'endangered-burl': - hidesRealTimePlayback: true - hidesCodeToolbar: true - requiredGear: {feet: 'leather-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-ii', eyes: 'crude-glasses'} - restrictedGear: {feet: 'simple-boots', 'right-hand': 'crude-builders-hammer', 'programming-book': 'programmaticon-i'} - 'village-guard': - hidesCodeToolbar: true - requiredGear: {feet: 'leather-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-ii', eyes: 'crude-glasses'} - restrictedGear: {feet: 'simple-boots', 'right-hand': 'crude-builders-hammer'} - 'thornbush-farm': - hidesCodeToolbar: true - requiredGear: {feet: 'leather-boots', 'right-hand': 'crude-builders-hammer', 'programming-book': 'programmaticon-ii', eyes: 'crude-glasses'} - restrictedGear: {feet: 'simple-boots', 'right-hand': 'simple-sword'} - requiredCode: ['topEnemy'] - 'back-to-back': - hidesCodeToolbar: true - requiredGear: {feet: 'leather-boots', torso: 'tarnished-bronze-breastplate', waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'crude-glasses', 'right-hand': 'simple-sword', 'left-hand': 'wooden-shield'} - restrictedGear: {feet: 'simple-boots', 'right-hand': 'crude-builders-hammer'} - 'ogre-encampment': - requiredGear: {torso: 'tarnished-bronze-breastplate', waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'crude-glasses', 'right-hand': 'simple-sword', 'left-hand': 'wooden-shield'} - restrictedGear: {feet: 'simple-boots', 'right-hand': 'crude-builders-hammer'} - 'woodland-cleaver': - requiredGear: {torso: 'tarnished-bronze-breastplate', waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'crude-glasses', 'right-hand': 'long-sword', 'left-hand': 'wooden-shield', wrists: 'sundial-wristwatch', feet: 'leather-boots'} - restrictedGear: {feet: 'simple-boots', 'right-hand': 'simple-sword'} - 'shield-rush': - requiredGear: {torso: 'tarnished-bronze-breastplate', waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'crude-glasses', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield', wrists: 'sundial-wristwatch'} - restrictedGear: {'left-hand': 'wooden-shield'} - - # Warrior branch - 'peasant-protection': - requiredGear: {torso: 'tarnished-bronze-breastplate', waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield', wrists: 'sundial-wristwatch'} - restrictedGear: {eyes: 'crude-glasses'} - 'munchkin-swarm': - requiredGear: {torso: 'tarnished-bronze-breastplate', waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield', wrists: 'sundial-wristwatch'} - restrictedGear: {} - - # Ranger branch - 'munchkin-harvest': - requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield', wrists: 'sundial-wristwatch'} - restrictedGear: {} - 'swift-dagger': - requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'crude-crossbow', 'left-hand': 'crude-dagger', wrists: 'sundial-wristwatch'} - restrictedGear: {eyes: 'crude-glasses'} - allowedHeroes: ['ninja', 'trapper', 'forest-archer'] - 'shrapnel': - requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'crude-crossbow', 'left-hand': 'weak-charge', wrists: 'sundial-wristwatch'} - restrictedGear: {eyes: 'crude-glasses', 'left-hand': 'crude-dagger'} - allowedHeroes: ['ninja', 'trapper', 'forest-archer'] - - # Wizard branch - 'arcane-ally': - requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield', wrists: 'sundial-wristwatch'} - restrictedGear: {eyes: 'crude-glasses'} - 'touch-of-death': - requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'enchanted-stick', 'left-hand': 'unholy-tome-i', wrists: 'sundial-wristwatch'} - restrictedGear: {} - allowedHeroes: ['librarian', 'potion-master', 'sorcerer'] - 'bonemender': - requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'enchanted-stick', 'left-hand': 'book-of-life-i', wrists: 'sundial-wristwatch'} - restrictedGear: {'left-hand': 'unholy-tome-i'} - requiredCode: ['canCast'] - allowedHeroes: ['librarian', 'potion-master', 'sorcerer'] - - 'coinucopia': - requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', 'programming-book': 'programmaticon-ii', flag: 'basic-flags'} - restrictedGear: {} - 'copper-meadows': - requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', 'programming-book': 'programmaticon-ii', flag: 'basic-flags', eyes: 'wooden-glasses'} - restrictedGear: {} - 'drop-the-flag': - requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', 'programming-book': 'programmaticon-ii', flag: 'basic-flags', eyes: 'wooden-glasses', 'right-hand': 'crude-builders-hammer'} - restrictedGear: {'right-hand': 'long-sword'} - 'deadly-pursuit': - requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', 'programming-book': 'programmaticon-ii', flag: 'basic-flags', eyes: 'wooden-glasses', 'right-hand': 'crude-builders-hammer'} - restrictedGear: {'right-hand': 'long-sword'} - 'rich-forager': - requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', 'programming-book': 'programmaticon-ii', flag: 'basic-flags', eyes: 'wooden-glasses', torso: 'tarnished-bronze-breastplate', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield'} - restrictedGear: {'right-hand': 'crude-builders-hammer'} - 'multiplayer-treasure-grove': - requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', 'programming-book': 'programmaticon-ii', flag: 'basic-flags', eyes: 'wooden-glasses', torso: 'tarnished-bronze-breastplate'} - restrictedGear: {} - 'siege-of-stonehold': - requiredGear: {} - restrictedGear: {} diff --git a/app/lib/LevelSetupManager.coffee b/app/lib/LevelSetupManager.coffee index 8ebea923d..42abe2beb 100644 --- a/app/lib/LevelSetupManager.coffee +++ b/app/lib/LevelSetupManager.coffee @@ -1,47 +1,71 @@ CocoClass = require 'core/CocoClass' PlayHeroesModal = require 'views/play/modal/PlayHeroesModal' -InventoryModal = require 'views/game-menu/InventoryModal' +InventoryModal = require 'views/play/menu/InventoryModal' +Level = require 'models/Level' LevelSession = require 'models/LevelSession' SuperModel = require 'models/SuperModel' ThangType = require 'models/ThangType' -LevelOptions = require 'lib/LevelOptions' lastHeroesEarned = me.get('earned')?.heroes ? [] lastHeroesPurchased = me.get('purchased')?.heroes ? [] - module.exports = class LevelSetupManager extends CocoClass constructor: (@options) -> super() @supermodel = @options.supermodel ? new SuperModel() @session = @options.session + @loadLevel() if @session @fillSessionWithDefaults() else @loadSession() - @loadModals() + + loadLevel: -> + levelURL = "/db/level/#{@options.levelID}" + @level = new Level().setURL levelURL + @level = @supermodel.loadModel(@level, 'level').model + onLevelSync = -> + return if @destroyed + if @waitingToLoadModals + @waitingToLoadModals = false + @loadModals() + onLevelSync.call @ if @level.loaded loadSession: -> - url = "/db/level/#{@options.levelID}/session" - #url += "?team=#{@team}" if @options.team # TODO: figure out how to get the teams for multiplayer PVP hero style - @session = new LevelSession().setURL url + sessionURL = "/db/level/#{@options.levelID}/session" + #sessionURL += "?team=#{@team}" if @options.team # TODO: figure out how to get the teams for multiplayer PVP hero style + @session = new LevelSession().setURL sessionURL onSessionSync = -> + return if @destroyed @session.url = -> '/db/level.session/' + @id @fillSessionWithDefaults() @listenToOnce @session, 'sync', onSessionSync @session = @supermodel.loadModel(@session, 'level_session').model - if @session.loaded - onSessionSync.call @ + onSessionSync.call @ if @session.loaded fillSessionWithDefaults: -> heroConfig = _.merge {}, me.get('heroConfig'), @session.get('heroConfig') @session.set('heroConfig', heroConfig) + if @level.loaded + @loadModals() + else + @waitingToLoadModals = true loadModals: -> # build modals and prevent them from disappearing. - @heroesModal = new PlayHeroesModal({supermodel: @supermodel, session: @session, confirmButtonI18N: 'play.next', levelID: @options.levelID, hadEverChosenHero: @options.hadEverChosenHero}) - @inventoryModal = new InventoryModal({supermodel: @supermodel, session: @session, levelID: @options.levelID}) + if @level.get('slug') is 'zero-sum' + sorcerer = '52fd1524c7e6cf99160e7bc9' + if @session.get('creator') is '532dbc73a622924444b68ed9' # Wizard Dude gets his own avatar + sorcerer = '53e126a4e06b897606d38bef' + @session.set 'heroConfig', {"thangType":sorcerer,"inventory":{"misc-0":"53e2396a53457600003e3f0f","programming-book":"546e266e9df4a17d0d449be5","minion":"54eb5dbc49fa2d5c905ddf56","feet":"53e214f153457600003e3eab","right-hand":"54eab7f52b7506e891ca7202","left-hand":"5463758f3839c6e02811d30f","wrists":"54693797a2b1f53ce79443e9","gloves":"5469425ca2b1f53ce7944421","torso":"546d4a549df4a17d0d449a97","neck":"54693274a2b1f53ce79443c9","eyes":"546941fda2b1f53ce794441d","head":"546d4ca19df4a17d0d449abf"}} + @onInventoryModalPlayClicked() + return + if @level.get('type', true) in ['course', 'course-ladder'] + @onInventoryModalPlayClicked() + return + @heroesModal = new PlayHeroesModal({supermodel: @supermodel, session: @session, confirmButtonI18N: 'play.next', level: @level, hadEverChosenHero: @options.hadEverChosenHero}) + @inventoryModal = new InventoryModal({supermodel: @supermodel, session: @session, level: @level}) @heroesModalDestroy = @heroesModal.destroy @inventoryModalDestroy = @inventoryModal.destroy @heroesModal.destroy = @inventoryModal.destroy = _.noop @@ -49,14 +73,19 @@ module.exports = class LevelSetupManager extends CocoClass @listenToOnce @heroesModal, 'hero-loaded', @onceHeroLoaded @listenTo @inventoryModal, 'choose-hero-click', @onChooseHeroClicked @listenTo @inventoryModal, 'play-click', @onInventoryModalPlayClicked + @modalsLoaded = true + if @waitingToOpen + @waitingToOpen = false + @open() open: -> + return @waitingToOpen = true unless @modalsLoaded firstModal = if @options.hadEverChosenHero then @inventoryModal else @heroesModal if (not _.isEqual(lastHeroesEarned, me.get('earned')?.heroes ? []) or not _.isEqual(lastHeroesPurchased, me.get('purchased')?.heroes ? [])) console.log 'Showing hero picker because heroes earned/purchased has changed.' firstModal = @heroesModal - else if allowedHeroSlugs = LevelOptions[@options.levelID]?.allowedHeroes + else if allowedHeroSlugs = @level.get 'allowedHeroes' unless _.find(allowedHeroSlugs, (slug) -> ThangType.heroes[slug] is me.get('heroConfig')?.thangType) firstModal = @heroesModal lastHeroesEarned = me.get('earned')?.heroes ? [] @@ -66,31 +95,30 @@ module.exports = class LevelSetupManager extends CocoClass # @inventoryModal.onShown() # replace? @playSound 'game-menu-open' - #- Modal events onceHeroLoaded: (e) -> - @inventoryModal.setHero(e.hero) + @inventoryModal.setHero(e.hero) if window.currentModal is @inventoryModal onHeroesModalConfirmClicked: (e) -> @options.parent.openModalView(@inventoryModal) @inventoryModal.render() @inventoryModal.didReappear() @inventoryModal.onShown() - @inventoryModal.setHero(e.hero) - window.tracker?.trackEvent 'Choose Inventory', category: 'Play Level', ['Google Analytics'] + @inventoryModal.setHero(e.hero) if e.hero + window.tracker?.trackEvent 'Choose Inventory', category: 'Play Level' onChooseHeroClicked: -> @options.parent.openModalView(@heroesModal) @heroesModal.render() @heroesModal.didReappear() @inventoryModal.endHighlight() - window.tracker?.trackEvent 'Change Hero', category: 'Play Level', ['Google Analytics'] + window.tracker?.trackEvent 'Change Hero', category: 'Play Level' onInventoryModalPlayClicked: -> @navigatingToPlay = true - PlayLevelView = require 'views/play/level/PlayLevelView' - LadderView = require 'views/play/ladder/LadderView' + PlayLevelView = 'views/play/level/PlayLevelView' + LadderView = 'views/ladder/LadderView' viewClass = if @options.levelPath is 'ladder' then LadderView else PlayLevelView Backbone.Mediator.publish 'router:navigate', { route: "/play/#{@options.levelPath || 'level'}/#{@options.levelID}" @@ -99,6 +127,6 @@ module.exports = class LevelSetupManager extends CocoClass } destroy: -> - @heroesModalDestroy.call @heroesModal unless @heroesModal.destroyed - @inventoryModalDestroy.call @inventoryModal unless @inventoryModal.destroyed + @heroesModalDestroy?.call @heroesModal unless @heroesModal?.destroyed + @inventoryModalDestroy?.call @inventoryModal unless @inventoryModal?.destroyed super() diff --git a/app/lib/aether_utils.coffee b/app/lib/aether_utils.coffee index d3db579f9..90c421d2d 100644 --- a/app/lib/aether_utils.coffee +++ b/app/lib/aether_utils.coffee @@ -9,6 +9,8 @@ module.exports.createAetherOptions = (options) -> functionName: options.functionName protectAPI: not options.skipProtectAPI includeFlow: Boolean options.includeFlow + noVariablesInFlow: true + skipDuplicateUserInfoInFlow: true # Optimization that won't work if we are stepping with frames yieldConditionally: options.functionName is 'plan' simpleLoops: true globals: ['Vector', '_'] @@ -22,7 +24,7 @@ module.exports.createAetherOptions = (options) -> aether_MissingThis: {level: 'error'} problemContext: options.problemContext #functionParameters: # TODOOOOO - executionLimit: 1 * 1000 * 1000 + executionLimit: 3 * 1000 * 1000 language: options.codeLanguage parameters = functionParameters[options.functionName] unless parameters diff --git a/app/lib/scripts/ScriptManager.coffee b/app/lib/scripts/ScriptManager.coffee index 9d6810203..937c6dbce 100644 --- a/app/lib/scripts/ScriptManager.coffee +++ b/app/lib/scripts/ScriptManager.coffee @@ -220,8 +220,8 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass @notifyScriptStateChanged() @scriptInProgress = true @currentTimeouts = [] - scriptLabel = "#{@levelID}: #{nextNoteGroup.scriptID} - #{nextNoteGroup.name}" - application.tracker?.trackEvent 'Script Started', {label: scriptLabel}, ['Google Analytics'] + scriptLabel = "#{nextNoteGroup.scriptID} - #{nextNoteGroup.name}" + application.tracker?.trackEvent 'Script Started', {levelID: @levelID, label: scriptLabel, ls: @session?.get('_id')}, ['Google Analytics'] console.debug "SCRIPT: Starting note group '#{nextNoteGroup.name}'" if @debugScripts for module in nextNoteGroup.modules @processNote(note, nextNoteGroup) for note in module.startNotes() @@ -259,7 +259,6 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass @publishNote(note) publishNote: (note) -> - Backbone.Mediator.publish 'playback:real-time-playback-ended', {} unless @session.get('heroConfig') # Only old levels need this, to stop interfering with old victory coolcams. Backbone.Mediator.publish note.channel, note.event ? {} # ENDING NOTES @@ -283,8 +282,8 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass return if @ending # kill infinite loops right here @ending = true return unless @currentNoteGroup? - scriptLabel = "#{@levelID}: #{@currentNoteGroup.scriptID} - #{@currentNoteGroup.name}" - application.tracker?.trackEvent 'Script Ended', {label: scriptLabel}, ['Google Analytics'] + scriptLabel = "#{@currentNoteGroup.scriptID} - #{@currentNoteGroup.name}" + application.tracker?.trackEvent 'Script Ended', {levelID: @levelID, label: scriptLabel, ls: @session?.get('_id')}, ['Google Analytics'] console.debug "SCRIPT: Ending note group '#{@currentNoteGroup.name}'" if @debugScripts clearTimeout(timeout) for timeout in @currentTimeouts for module in @currentNoteGroup.modules diff --git a/app/lib/scripts/ScriptModule.coffee b/app/lib/scripts/ScriptModule.coffee index 4c4348644..b848bf204 100644 --- a/app/lib/scripts/ScriptModule.coffee +++ b/app/lib/scripts/ScriptModule.coffee @@ -8,7 +8,7 @@ module.exports = class ScriptModule extends CocoClass constructor: (@noteGroup) -> super() if not @noteGroup.prepared - @analyzeNoteGroup(noteGroup) + @analyzeNoteGroup(@noteGroup) @noteGroup.notes ?= [] @noteGroup.prepared = true diff --git a/app/lib/simulator/Simulator.coffee b/app/lib/simulator/Simulator.coffee index b39d7f0e4..10ce5cdc6 100644 --- a/app/lib/simulator/Simulator.coffee +++ b/app/lib/simulator/Simulator.coffee @@ -5,12 +5,26 @@ GoalManager = require 'lib/world/GoalManager' God = require 'lib/God' {createAetherOptions} = require 'lib/aether_utils' +SIMULATOR_VERSION = 3 + +simulatorInfo = {} +if $.browser + simulatorInfo['desktop'] = $.browser.desktop if $.browser.desktop + simulatorInfo['name'] = $.browser.name if $.browser.name + simulatorInfo['platform'] = $.browser.platform if $.browser.platform + simulatorInfo['version'] = $.browser.versionNumber if $.browser.versionNumber + module.exports = class Simulator extends CocoClass constructor: (@options) -> @options ?= {} + simulatorType = if @options.headlessClient then 'headless' else 'browser' + @simulator = + type: simulatorType + version: SIMULATOR_VERSION + info: simulatorInfo _.extend @, Backbone.Events @trigger 'statusUpdate', 'Starting simulation!' - @retryDelayInSeconds = 10 + @retryDelayInSeconds = 2 @taskURL = '/queue/scoring' @simulatedByYou = 0 @god = new God maxAngels: 1, workerCode: @options.workerCode, headless: true # Start loading worker. @@ -28,10 +42,17 @@ module.exports = class Simulator extends CocoClass type: 'POST' parse: true data: - 'humansGameID': humanGameID - 'ogresGameID': ogresGameID + humansGameID: humanGameID + ogresGameID: ogresGameID + simulator: @simulator error: (errorData) -> console.warn "There was an error fetching two games! #{JSON.stringify errorData}" + if errorData?.responseText?.indexOf("Old simulator") isnt -1 + noty { + text: errorData.responseText + layout: 'center' + type: 'error' + } success: (taskData) => return if @destroyed unless taskData @@ -85,7 +106,11 @@ module.exports = class Simulator extends CocoClass processSingleGameResults: (simulationResults) -> return console.error "Weird, we destroyed the Simulator before it processed results?" if @destroyed - taskResults = @formTaskResultsObject simulationResults + try + taskResults = @formTaskResultsObject simulationResults + catch error + console.log "Failed to form task results:", error + return @cleanupAndSimulateAnotherTask() console.log 'Processing results:', taskResults humanSessionRank = taskResults.sessions[0].metrics.rank ogreSessionRank = taskResults.sessions[1].metrics.rank @@ -132,6 +157,7 @@ module.exports = class Simulator extends CocoClass parse: true error: @handleFetchTaskError success: @setupSimulationAndLoadLevel + cache: false handleFetchTaskError: (errorData) => console.error "There was a horrible Error: #{JSON.stringify errorData}" @@ -143,7 +169,6 @@ module.exports = class Simulator extends CocoClass console.log info @trigger 'statusUpdate', info @fetchAndSimulateOneGame() - application.tracker?.trackEvent 'Simulator Result', label: 'No Games', ['Google Analytics'] simulateAnotherTaskAfterDelay: => console.log "Retrying in #{@retryDelayInSeconds}" @@ -199,6 +224,12 @@ module.exports = class Simulator extends CocoClass @god.setLevelSessionIDs (session.sessionID for session in @task.getSessions()) @god.setWorldClassMap @world.classMap @god.setGoalManager new GoalManager(@world, @level.get 'goals') + humanFlagHistory = _.filter @session.get('state')?.flagHistory ? [], (event) => event.source isnt 'code' and event.team is (@session.get('team') ? 'humans') + ogreFlagHistory = _.filter @otherSession.get('state')?.flagHistory ? [], (event) => event.source isnt 'code' and event.team is (@otherSession.get('team') ? 'ogres') + @god.lastFlagHistory = humanFlagHistory.concat ogreFlagHistory + #console.log 'got flag history', @god.lastFlagHistory, 'from', humanFlagHistory, ogreFlagHistory, @session.get('state'), @otherSession.get('state') + @god.lastSubmissionCount = 0 # TODO: figure out how to combine submissionCounts from both players so we can use submissionCount random seeds again. + @god.lastDifficulty = 0 commenceSimulationAndSetupCallback: -> Backbone.Mediator.subscribeOnce 'god:infinite-loop', @onInfiniteLoop, @ @@ -237,7 +268,11 @@ module.exports = class Simulator extends CocoClass processResults: (simulationResults) -> return console.error "Weird, we destroyed the Simulator before it processed results?" if @destroyed - taskResults = @formTaskResultsObject simulationResults + try + taskResults = @formTaskResultsObject simulationResults + catch error + console.log "Failed to form task results:", error + return @cleanupAndSimulateAnotherTask() unless taskResults.taskID console.error "*** Error: taskResults has no taskID ***\ntaskResults:", taskResults @cleanupAndSimulateAnotherTask() @@ -269,12 +304,10 @@ module.exports = class Simulator extends CocoClass return if @destroyed console.log "Task registration result: #{JSON.stringify result}" @trigger 'statusUpdate', 'Results were successfully sent back to server!' - console.log 'Simulated by you:', @simulatedByYou @simulatedByYou++ unless @options.headlessClient simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1 $('#simulated-by-you').text(simulatedBy) - application.tracker?.trackEvent 'Simulator Result', label: 'Success', ['Google Analytics'] handleTaskResultsTransferError: (error) => return if @destroyed @@ -298,6 +331,8 @@ module.exports = class Simulator extends CocoClass originalSessionRank: -1 calculationTime: 500 sessions: [] + simulator: @simulator + randomSeed: @task.world.randomSeed for session in @task.getSessions() sessionResult = diff --git a/app/lib/sprites/SpriteParser.coffee b/app/lib/sprites/SpriteParser.coffee index 361dc8411..d79436609 100644 --- a/app/lib/sprites/SpriteParser.coffee +++ b/app/lib/sprites/SpriteParser.coffee @@ -37,14 +37,7 @@ module.exports = class SpriteParser blocks = @findBlocks ast, source containers = _.filter blocks, {kind: 'Container'} movieClips = _.filter blocks, {kind: 'MovieClip'} - if movieClips.length - # First movie clip is root, so do it last - movieClips = movieClips[1 ... movieClips.length].concat([movieClips[0]]) - - # first container isn't necessarily root... actually the last one is root in blue-cart -# else if containers.length -# # First container is root, so do it last -# containers = containers[1 ... containers.length].concat([containers[0]]) + mainClip = _.last(movieClips) ? _.last(containers) @animationName = mainClip.name for container, index in containers @@ -69,17 +62,24 @@ module.exports = class SpriteParser break continue unless container.bounds and instructions.length @addContainer {c: instructions, b: container.bounds}, container.name + + childrenMovieClips = [] + for movieClip, index in movieClips - if index is 0 - for bounds in movieClip.frameBounds - bounds[0] -= @width / 2 - bounds[1] -= @height / 2 - movieClip.bounds[0] -= @width / 2 - movieClip.bounds[1] -= @height / 2 + lastBounds = null + # fill in bounds which are null... + for bounds, boundsIndex in movieClip.frameBounds + if not bounds + movieClip.frameBounds[boundsIndex] = _.clone(lastBounds) + else + lastBounds = bounds + localGraphics = @getGraphicsFromBlock(movieClip, source) [shapeKeys, localShapes] = @getShapesFromBlock movieClip, source localContainers = @getContainersFromMovieClip movieClip, source, true localAnimations = @getAnimationsFromMovieClip movieClip, source, true + for animation in localAnimations + childrenMovieClips.push(animation.gn) localTweens = @getTweensFromMovieClip movieClip, source, localShapes, localContainers, localAnimations @addAnimation { shapes: localShapes @@ -91,6 +91,14 @@ module.exports = class SpriteParser frameBounds: movieClip.frameBounds }, movieClip.name + for movieClip in movieClips + if movieClip.name not in childrenMovieClips + for bounds in movieClip.frameBounds + bounds[0] -= @width / 2 + bounds[1] -= @height / 2 + movieClip.bounds[0] -= @width / 2 + movieClip.bounds[1] -= @height / 2 + @saveToModel() return movieClips[0]?.name @@ -382,7 +390,7 @@ module.exports = class SpriteParser name = node.callee.property?.name return unless name in ['get', 'to', 'wait'] return if name is 'get' and callExpressions.length # avoid Ease calls in the tweens - flattenedRanges = _.flatten [a.range for a in node.arguments] + flattenedRanges = _.flatten [(a.range for a in node.arguments)] range = [_.min(flattenedRanges), _.max(flattenedRanges)] # Replace 'this.<local>' references with just the 'name' argsSource = @subSourceFromRange(range, source) diff --git a/app/lib/surface/Dimmer.coffee b/app/lib/surface/Dimmer.coffee index 8855e5948..bdbac64c1 100644 --- a/app/lib/surface/Dimmer.coffee +++ b/app/lib/surface/Dimmer.coffee @@ -48,7 +48,6 @@ module.exports = class Dimmer extends CocoClass @updateDimMask() if @on setSprites: (@sprites) -> - console.log dim: -> @on = true @@ -65,7 +64,7 @@ module.exports = class Dimmer extends CocoClass updateDimMask: => @dimMask.graphics.clear() for thangID, sprite of @sprites - continue unless (thangID in @highlightedThangIDs) or sprite.isTalking?() or sprite.thang?.id is 'My Wizard' + continue unless (thangID in @highlightedThangIDs) or sprite.isTalking?() sup = x: sprite.sprite.x, y: sprite.sprite.y cap = @camera.surfaceToCanvas sup r = 50 * @camera.zoom # TODO: find better way to get the radius based on the sprite's size diff --git a/app/lib/surface/Label.coffee b/app/lib/surface/Label.coffee index b745fb63c..1aeed7d1c 100644 --- a/app/lib/surface/Label.coffee +++ b/app/lib/surface/Label.coffee @@ -47,7 +47,7 @@ module.exports = class Label extends CocoClass @layer.updateLayerOrder() update: -> - return unless @text + return unless @text and @sprite.sprite offset = @sprite.getOffset? (if @style in ['dialogue', 'say'] then 'mouth' else 'aboveHead') offset ?= x: 0, y: 0 # temp (if not Lank) rotation = @sprite.getRotation() diff --git a/app/lib/surface/Lank.coffee b/app/lib/surface/Lank.coffee index 7e673bc53..c64591207 100644 --- a/app/lib/surface/Lank.coffee +++ b/app/lib/surface/Lank.coffee @@ -30,6 +30,7 @@ module.exports = Lank = class Lank extends CocoClass thang: null camera: null showInvisible: false + preloadSounds: true possessed: false flipped: false @@ -84,8 +85,9 @@ module.exports = Lank = class Lank extends CocoClass onThangTypeLoaded: -> @stillLoading = false - for trigger, sounds of @thangType.get('soundTriggers') or {} when trigger isnt 'say' - AudioPlayer.preloadSoundReference sound for sound in sounds when sound + if @options.preloadSounds + for trigger, sounds of @thangType.get('soundTriggers') or {} when trigger isnt 'say' + AudioPlayer.preloadSoundReference sound for sound in sounds when sound if @thangType.get('raster') @actions = {} @isRaster = true @@ -101,6 +103,7 @@ module.exports = Lank = class Lank extends CocoClass setSprite: (newSprite) -> if @sprite + @sprite.off 'animationend', @playNextAction @sprite.destroy?() if parent = @sprite.parent parent.removeChild @sprite @@ -138,6 +141,7 @@ module.exports = Lank = class Lank extends CocoClass onSurfaceTicked: (e) -> @age += e.dt playNextAction: => + return if @destroyed @playAction(@actionQueue.splice(0, 1)[0]) if @actionQueue.length playAction: (action) -> @@ -197,50 +201,56 @@ module.exports = Lank = class Lank extends CocoClass showAreaOfEffects: -> return unless @thang?.currentEvents for event in @thang.currentEvents - continue unless event.startsWith 'aoe-' + continue unless _.string.startsWith event, 'aoe-' continue if @handledDisplayEvents[event] @handledDisplayEvents[event] = true args = JSON.parse(event[4...]) key = 'aoe-' + JSON.stringify(args[2..]) + layerName = args[6] ? 'ground' # Can also specify 'floating'. + unless layer = @options[layerName + 'Layer'] + console.error "#{@thang.id} couldn't find layer #{layerName}Layer for AOE effect #{key}; using ground layer." + layer = @options.groundLayer - unless key in @options.groundLayer.spriteSheet.getAnimations() - args = JSON.parse(event[4...]) + unless key in layer.spriteSheet.getAnimations() circle = new createjs.Shape() radius = args[2] * Camera.PPM if args.length is 4 circle.graphics.beginFill(args[3]).drawCircle(0, 0, radius) else - startAngle = args[4] - endAngle = args[5] + startAngle = args[4] or 0 + endAngle = args[5] or 2 * Math.PI + if startAngle is endAngle + startAngle = 0 + endAngle = 2 * Math.PI circle.graphics.beginFill(args[3]) .lineTo(0, 0) .lineTo(radius * Math.cos(startAngle), radius * Math.sin(startAngle)) .arc(0, 0, radius, startAngle, endAngle) .lineTo(0, 0) - @options.groundLayer.addCustomGraphic(key, circle, [-radius, -radius, radius*2, radius*2]) + layer.addCustomGraphic(key, circle, [-radius, -radius, radius*2, radius*2]) - circle = new createjs.Sprite(@options.groundLayer.spriteSheet) + circle = new createjs.Sprite(layer.spriteSheet) circle.gotoAndStop(key) pos = @options.camera.worldToSurface {x: args[0], y: args[1]} circle.x = pos.x circle.y = pos.y - resFactor = @options.groundLayer.resolutionFactor + resFactor = layer.resolutionFactor circle.scaleY = @options.camera.y2x * 0.7 / resFactor circle.scaleX = 0.7 / resFactor circle.alpha = 0.2 - @options.groundLayer.addChild circle + layer.addChild circle createjs.Tween.get(circle) .to({alpha: 0.6, scaleY: @options.camera.y2x / resFactor, scaleX: 1 / resFactor}, 100, createjs.Ease.circOut) .to({alpha: 0, scaleY: 0, scaleX: 0}, 700, createjs.Ease.circIn) .call => return if @destroyed - @options.groundLayer.removeChild circle + layer.removeChild circle delete @handledDisplayEvents[event] showTextEvents: -> return unless @thang?.currentEvents for event in @thang.currentEvents - continue unless event.startsWith 'text-' + continue unless _.string.startsWith event, 'text-' continue if @handledDisplayEvents[event] @handledDisplayEvents[event] = true options = JSON.parse(event[5...]) @@ -287,21 +297,21 @@ module.exports = Lank = class Lank extends CocoClass # Let the pending flags know we're here (but not this call stack, they need to delete themselves, and we may be iterating sprites). _.defer => Backbone.Mediator.publish 'surface:flag-appeared', sprite: @ - updateScale: -> + updateScale: (force) -> return unless @sprite - if @thangType.get('matchWorldDimensions') and @thang - if @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight + if @thangType.get('matchWorldDimensions') and @thang and @options.camera + if force or @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight or @thang.rotation isnt @lastThangRotation bounds = @sprite.getBounds() return unless bounds - @sprite.scaleX = @thang.width * Camera.PPM / bounds.width - @sprite.scaleY = @thang.height * Camera.PPM * @options.camera.y2x / bounds.height - @sprite.regX = bounds.width / 2 - @sprite.regY = bounds.height / 2 + @sprite.scaleX = @thang.width * Camera.PPM / bounds.width * (@options.camera.y2x + (1 - @options.camera.y2x) * Math.abs Math.cos @thang.rotation) + @sprite.scaleY = @thang.height * Camera.PPM / bounds.height * (@options.camera.y2x + (1 - @options.camera.y2x) * Math.abs Math.sin @thang.rotation) + @sprite.regX = bounds.width * 3 / 4 # Why not / 2? I don't know. + @sprite.regY = bounds.height * 3 / 4 # Why not / 2? I don't know. unless @thang.spriteName is 'Beam' @sprite.scaleX *= @thangType.get('scale') ? 1 @sprite.scaleY *= @thangType.get('scale') ? 1 - [@lastThangWidth, @lastThangHeight] = [@thang.width, @thang.height] + [@lastThangWidth, @lastThangHeight, @lastThangRotation] = [@thang.width, @thang.height, @thang.rotation] return scaleX = scaleY = 1 @@ -326,7 +336,7 @@ module.exports = Lank = class Lank extends CocoClass newScaleFactorX = @thang?.scaleFactorX ? @thang?.scaleFactor ? 1 newScaleFactorY = @thang?.scaleFactorY ? @thang?.scaleFactor ? 1 - if @thang?.spriteName is 'Beam' + if @layer?.name is 'Land' or @thang?.spriteName is 'Beam' @scaleFactorX = newScaleFactorX @scaleFactorY = newScaleFactorY else if @thang and (newScaleFactorX isnt @targetScaleFactorX or newScaleFactorY isnt @targetScaleFactorY) @@ -466,6 +476,8 @@ module.exports = Lank = class Lank extends CocoClass bar.scaleX = healthPct / @options.floatingLayer.resolutionFactor if @thang.showsName @setNameLabel(if @thang.health <= 0 then '' else @thang.id) + else if @options.playerName + @setNameLabel @options.playerName configureMouse: -> @sprite.cursor = 'pointer' if @thang?.isSelectable @@ -560,8 +572,8 @@ module.exports = Lank = class Lank extends CocoClass updateMarks: -> return unless @options.camera - @addMark 'repair', null, 'repair' if @thang?.errorsOut - @marks.repair?.toggle @thang?.errorsOut + @addMark 'repair', null, 'repair' if @thang?.erroredOut + @marks.repair?.toggle @thang?.erroredOut if @selected @marks[range['name']].toggle true for range in @ranges @@ -691,10 +703,13 @@ module.exports = Lank = class Lank extends CocoClass Backbone.Mediator.publish 'surface:gold-changed', {team: @thang.team, gold: gold, goldEarned: Math.floor(@thang.goldEarned ? 0)} shouldMuteMessage: (m) -> - return true if m in ['moveRight', 'moveUp', 'moveDown', 'moveLeft'] - return true if /^attack /.test m - return true if /^Repeating loop/.test m - return true if /^findNearestEnemy/.test m + if me.getAnnouncesActionAudioGroup() in ['no-audio', 'just-take-damage'] + return true if m in ['moveRight', 'moveUp', 'moveDown', 'moveLeft'] + return true if /^attack /.test m + return true if /^Repeating loop/.test m + return true if /^findNearestEnemy/.test m + + return false if m in ['moveRight', 'moveUp', 'moveDown', 'moveLeft'] @previouslySaidMessages ?= {} t0 = @previouslySaidMessages[m] ? 0 t1 = new Date() @@ -704,7 +719,10 @@ module.exports = Lank = class Lank extends CocoClass playSounds: (withDelay=true, volume=1.0) -> for event in @thang.currentEvents ? [] - @playSound event, withDelay, volume + if event is 'take-damage' and me.getAnnouncesActionAudioGroup() in ['no-audio', 'without-take-damage'] + null # Skip playing it + else + @playSound event, withDelay, volume if event is 'pay-bounty-gold' and @thang.bountyGold > 25 and @thang.team isnt me.team AudioPlayer.playInterfaceSound 'coin_1', 0.25 if @thang.actionActivated and (action = @thang.getActionName()) isnt 'say' @@ -805,4 +823,5 @@ module.exports = Lank = class Lank extends CocoClass p.removeChild @healthBar if p = @healthBar?.parent @sprite?.off 'animationend', @playNextAction clearInterval @effectInterval if @effectInterval + @dialogueSoundInstance?.removeAllEventListeners() super() diff --git a/app/lib/surface/LankBoss.coffee b/app/lib/surface/LankBoss.coffee index c6202b535..bcf014ac4 100644 --- a/app/lib/surface/LankBoss.coffee +++ b/app/lib/surface/LankBoss.coffee @@ -1,17 +1,14 @@ CocoClass = require 'core/CocoClass' {me} = require 'core/auth' LayerAdapter = require './LayerAdapter' -IndieLank = require 'lib/surface/IndieLank' -WizardLank = require 'lib/surface/WizardLank' FlagLank = require 'lib/surface/FlagLank' Lank = require 'lib/surface/Lank' Mark = require './Mark' Grid = require 'lib/world/Grid' +utils = require 'core/utils' module.exports = class LankBoss extends CocoClass subscriptions: - 'bus:player-joined': 'onPlayerJoined' - 'bus:player-left': 'onPlayerLeft' 'level:set-debug': 'onSetDebug' 'sprite:highlight-sprites': 'onHighlightSprites' 'surface:stage-mouse-down': 'onStageMouseDown' @@ -35,11 +32,10 @@ module.exports = class LankBoss extends CocoClass @camera = @options.camera @webGLStage = @options.webGLStage @surfaceTextLayer = @options.surfaceTextLayer - @world = options.world + @world = @options.world @options.thangTypes ?= [] @lanks = {} @lankArray = [] # Mirror @lanks, but faster for when we just need to iterate - @selfWizardLank = null @createLayers() @pendingFlags = [] @@ -86,7 +82,7 @@ module.exports = class LankBoss extends CocoClass console.error 'Lank collision! Already have:', id if @lanks[id] @lanks[id] = lank @lankArray.push lank - layer ?= @layerAdapters['Obstacle'] if lank.thang?.spriteName.search(/(dungeon|indoor).wall/i) isnt -1 + layer ?= @layerAdapters['Obstacle'] if lank.thang?.spriteName.search(/(dungeon|indoor|ice).wall/i) isnt -1 layer ?= @layerForChild lank.sprite, lank layer.addLank lank layer.updateLayerOrder() @@ -99,49 +95,6 @@ module.exports = class LankBoss extends CocoClass createLankOptions: (options) -> _.extend options, camera: @camera, resolutionFactor: SPRITE_RESOLUTION_FACTOR, groundLayer: @layerAdapters['Ground'], textLayer: @surfaceTextLayer, floatingLayer: @layerAdapters['Floating'], showInvisible: @options.showInvisible - createIndieLanks: (indieLanks, withWizards) -> - unless @indieLanks - @indieLanks = [] - @indieLanks = (@createIndieLank indieLank for indieLank in indieLanks) if indieLanks - if withWizards and not @selfWizardLank - @selfWizardLank = @createWizardLank thangID: 'My Wizard', isSelf: true, lanks: @lanks - - createIndieLank: (indieLank) -> - unless thangType = @thangTypeFor indieLank.thangType - console.warn "Need to convert #{indieLank.id}'s ThangType #{indieLank.thangType} to a ThangType reference. Until then, #{indieLank.id} won't show up." - return - lank = new IndieLank thangType, @createLankOptions {thangID: indieLank.id, pos: indieLank.pos, lanks: @lanks, team: indieLank.team, teamColors: @world.getTeamColors()} - @addLank lank, lank.thang.id - - createOpponentWizard: (opponent) -> - # TODO: colorize name and cloud by team, colorize wizard by user's color config, level-specific wizard spawn points - lank = @createWizardLank thangID: opponent.id, name: opponent.name, codeLanguage: opponent.codeLanguage - if not opponent.levelSlug or opponent.levelSlug is 'brawlwood' - lank.targetPos = if opponent.team is 'ogres' then {x: 52, y: 52} else {x: 28, y: 28} - else if opponent.levelSlug in ['dungeon-arena', 'sky-span'] - lank.targetPos = if opponent.team is 'ogres' then {x: 72, y: 39} else {x: 9, y: 39} - else if opponent.levelSlug is 'criss-cross' - lank.targetPos = if opponent.team is 'ogres' then {x: 50, y: 12} else {x: 0, y: 40} - else - lank.targetPos = if opponent.team is 'ogres' then {x: 52, y: 28} else {x: 20, y: 28} - - createWizardLank: (options) -> - lank = new WizardLank @thangTypeFor('Wizard'), @createLankOptions(options) - @addLank lank, lank.thang.id, @layerAdapters['Floating'] - - onPlayerJoined: (e) -> - # Create another WizardLank, unless this player is just me - pid = e.player.id - return if pid is me.id - wiz = @createWizardLank thangID: pid, lanks: @lanks - wiz.animateIn() - state = e.player.wizard or {} - wiz.setInitialState(state.targetPos, @lanks[state.targetLank]) - - onPlayerLeft: (e) -> - pid = e.player.id - @lanks[pid]?.animateOut => @removeLank @lanks[pid] - onSetDebug: (e) -> return if e.debug is @debug @debug = e.debug @@ -162,6 +115,8 @@ module.exports = class LankBoss extends CocoClass options = @createLankOptions thang: thang options.resolutionFactor = if thangType.get('kind') is 'Floor' then 2 else SPRITE_RESOLUTION_FACTOR + if @options.playerNames and /Hero Placeholder/.test thang.id + options.playerName = @options.playerNames[thang.team] lank = new Lank thangType, options @listenTo lank, 'sprite:mouse-up', @onLankMouseUp @addLank lank, null, layer @@ -192,7 +147,7 @@ module.exports = class LankBoss extends CocoClass updatedObstacles = [] itemsJustEquipped = [] for thang in @world.thangs when thang.exists and thang.pos - itemsJustEquipped = itemsJustEquipped.concat @equipNewItems thang + itemsJustEquipped = itemsJustEquipped.concat @equipNewItems thang if thang.equip if lank = @lanks[thang.id] lank.setThang thang # make sure Lank has latest Thang else @@ -213,6 +168,24 @@ module.exports = class LankBoss extends CocoClass if @willSelectThang and @lanks[@willSelectThang[0]] @selectThang @willSelectThang... + @updateScreenReader() + + updateScreenReader: -> + # Testing ASCII map for screen readers + return unless me.get('name') is 'zersiax' #in ['zersiax', 'Nick'] + ascii = $('#ascii-surface') + thangs = (lank.thang for lank in @lankArray) + grid = new Grid thangs, @world.width, @world.height, 0, 0, 0, true + utils.replaceText ascii, grid.toString true + ascii.css 'transform', 'initial' + fullWidth = ascii.innerWidth() + fullHeight = ascii.innerHeight() + availableWidth = ascii.parent().innerWidth() + availableHeight = ascii.parent().innerHeight() + scale = availableWidth / fullWidth + scale = Math.min scale, availableHeight / fullHeight + ascii.css 'transform', "scale(#{scale})" + equipNewItems: (thang) -> itemsJustEquipped = [] if thang.equip and not thang.equipped @@ -231,7 +204,7 @@ module.exports = class LankBoss extends CocoClass cacheObstacles: (updatedObstacles=null) -> return if @cachedObstacles and not updatedObstacles lankArray = @lankArray - wallLanks = (lank for lank in lankArray when lank.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1) + wallLanks = (lank for lank in lankArray when lank.thangType?.get('name').search(/(dungeon|indoor|ice).wall/i) isnt -1) return if _.any (s.stillLoading for s in wallLanks) walls = (lank.thang for lank in wallLanks) @world.calculateBounds() @@ -255,6 +228,8 @@ module.exports = class LankBoss extends CocoClass onNewWorld: (e) -> @world = @options.world = e.world + # Clear obstacle cache for this level, since we are spawning walls dynamically + @cachedObstacles = false if e.finished and /kithgard-mastery/.test window.location.href play: -> lank.play() for lank in @lankArray diff --git a/app/lib/surface/LayerAdapter.coffee b/app/lib/surface/LayerAdapter.coffee index da2d88d3b..e867b65eb 100644 --- a/app/lib/surface/LayerAdapter.coffee +++ b/app/lib/surface/LayerAdapter.coffee @@ -199,7 +199,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass @upsertActionToRender(lank.thangType) else for action in _.values(lank.thangType.getActions()) - continue unless _.any @defaultActions, (prefix) -> action.name.startsWith(prefix) + continue unless _.any @defaultActions, (prefix) -> _.string.startsWith(action.name, prefix) @upsertActionToRender(lank.thangType, action.name, lank.options.colorConfig) upsertActionToRender: (thangType, actionName, colorConfig) -> @@ -500,6 +500,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass lank.setSprite(sprite) lank.update(true) @container.addChild(sprite) + lank.updateScale true if lank.thangType.get 'matchWorldDimensions' # Otherwise it's at the wrong scale for some reason. renderGroupingKey: (thangType, grouping, colorConfig) -> key = thangType.get('slug') diff --git a/app/lib/surface/Mark.coffee b/app/lib/surface/Mark.coffee index 39b4b4c64..b45e3d994 100644 --- a/app/lib/surface/Mark.coffee +++ b/app/lib/surface/Mark.coffee @@ -216,11 +216,19 @@ module.exports = class Mark extends CocoClass buildDebug: -> shapeName = if @lank.thang.shape in ['ellipsoid', 'disc'] then 'ellipse' else 'rect' - key = "#{shapeName}-debug" + key = "#{shapeName}-debug-#{@lank.thang.collisionCategory}" DEBUG_SIZE = 10 unless key in @layer.spriteSheet.getAnimations() shape = new createjs.Shape() - shape.graphics.beginFill 'rgba(171,205,239,0.5)' + debugColor = { + none: 'rgba(224,255,239,0.25)' + ground: 'rgba(239,171,205,0.5)' + air: 'rgba(131,205,255,0.5)' + ground_and_air: 'rgba(2391,140,239,0.5)' + obstacles: 'rgba(88,88,88,0.5)' + dead: 'rgba(89,171,100,0.25)' + }[@lank.thang.collisionCategory] or 'rgba(171,205,239,0.5)' + shape.graphics.beginFill debugColor bounds = [-DEBUG_SIZE / 2, -DEBUG_SIZE / 2, DEBUG_SIZE, DEBUG_SIZE] if shapeName is 'ellipse' shape.graphics.drawEllipse bounds... @@ -232,9 +240,11 @@ module.exports = class Mark extends CocoClass @sprite = new createjs.Sprite(@layer.spriteSheet) @sprite.gotoAndStop(key) PX = 3 - [w, h] = [Math.max(PX, @lank.thang.width * Camera.PPM), Math.max(PX, @lank.thang.height * Camera.PPM) * @camera.y2x] # TODO: doesn't work with rotation + w = Math.max(PX, @lank.thang.width * Camera.PPM) * (@camera.y2x + (1 - @camera.y2x) * Math.abs Math.cos @lank.thang.rotation) + h = Math.max(PX, @lank.thang.height * Camera.PPM) * (@camera.y2x + (1 - @camera.y2x) * Math.abs Math.sin @lank.thang.rotation) @sprite.scaleX = w / (@layer.resolutionFactor * DEBUG_SIZE) @sprite.scaleY = h / (@layer.resolutionFactor * DEBUG_SIZE) + @sprite.rotation = -@lank.thang.rotation * 180 / Math.PI buildSprite: -> if _.isString @thangType diff --git a/app/lib/surface/MusicPlayer.coffee b/app/lib/surface/MusicPlayer.coffee index 2f2b84eb1..b218ea05b 100644 --- a/app/lib/surface/MusicPlayer.coffee +++ b/app/lib/surface/MusicPlayer.coffee @@ -27,7 +27,7 @@ module.exports = class MusicPlayer extends CocoClass onPlayMusic: (e) -> return if application.isIPadApp # Hard to measure, but just guessing this will save memory. src = e.file - src = "/file#{e.file}#{AudioPlayer.ext}" + src = "/file#{src}#{AudioPlayer.ext}" if (not e.file) or src is @currentMusic?.src if e.play then @restartCurrentMusic() else @fadeOutCurrentMusic() return @@ -86,8 +86,7 @@ module.exports = class MusicPlayer extends CocoClass return if @inMenu @inMenu = true @previousMusic = @currentMusic - terrain = (e.terrain ? 'Dungeon').toLowerCase() - file = "/music/music-menu-#{terrain}" + file = "/music/music-menu" Backbone.Mediator.publish 'music-player:play-music', file: file, play: true, delay: 1000 onExitMenu: (e) -> diff --git a/app/lib/surface/SegmentedSprite.coffee b/app/lib/surface/SegmentedSprite.coffee index ad280910e..c982a3f60 100644 --- a/app/lib/surface/SegmentedSprite.coffee +++ b/app/lib/surface/SegmentedSprite.coffee @@ -20,14 +20,14 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer @handleTick = undefined @baseMovieClip.inUse = false if @baseMovieClip @removeAllEventListeners() - + # CreateJS.Sprite-like interface - + play: -> @paused = false unless @baseMovieClip and @animLength > 1 stop: -> @paused = true gotoAndPlay: (actionName) -> @goto(actionName, false) gotoAndStop: (actionName) -> @goto(actionName, true) - + goto: (actionName, @paused=true) -> @removeAllChildren() @currentAnimation = actionName @@ -38,12 +38,12 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer @actionNotSupported = false action = @thangType.getActions()[actionName] - randomStart = actionName.startsWith('move') - + randomStart = _.string.startsWith(actionName, 'move') + # because the resulting segmented image is set to the size of the movie clip, you can use # the raw registration data without scaling it. reg = action.positions?.registration or @thangType.get('positions')?.registration or {x:0, y:0} - + if action.animation @regX = -reg.x @regY = -reg.y @@ -55,7 +55,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer @frames = (parseInt(f) for f in @frames.split(',')) if @frames @animLength = if @frames then @frames.length else @baseMovieClip.timeline.duration @paused = true if @animLength is 1 - + if @frames if randomStart @currentFrame = @frames[_.random(@frames.length - 1)] @@ -64,7 +64,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer else if randomStart @currentFrame = then Math.floor(Math.random() * @animLength) - else + else @currentFrame = 0 @baseMovieClip.specialGoToAndStop(@currentFrame) @@ -73,13 +73,13 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer movieClip.specialGoToAndStop(movieClip.startPosition) else movieClip.specialGoToAndStop(@currentFrame) - + @takeChildrenFromMovieClip(@baseMovieClip, @) @loop = action.loops isnt false @goesTo = action.goesTo @notifyActionNeedsRender(action) if @actionNotSupported @scaleX = @scaleY = action.scale ? @thangType.get('scale') ? 1 - + else if action.container # All transformations will be done to the child sprite @regX = @regY = 0 @@ -105,17 +105,17 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer sprite.scaleX = sprite.scaleY = 1 / @resolutionFactor @children = [] @addChild(sprite) - + else if action.goesTo @goto(action.goesTo, @paused) return - + @scaleX *= -1 if action.flipX @scaleY *= -1 if action.flipY @baseScaleX = @scaleX @baseScaleY = @scaleY return - + notifyActionNeedsRender: (action) -> @lank?.trigger('action-needs-render', @lank, action) @@ -127,7 +127,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer mc.gotoAndStop(mc.currentFrame+0.01) # just to make sure it has its children back @childMovieClips = mc.childMovieClips return mc - + raw = @thangType.get('raw') animData = raw.animations[animationName] @lastAnimData = animData @@ -144,7 +144,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer anim.initialize(mode ? createjs.MovieClip.INDEPENDENT, startPosition ? 0, loops ? true) anim.specialGoToAndStop = specialGoToAndStop - for tweenData in animData.tweens + for tweenData, i in animData.tweens stopped = false tween = createjs.Tween for func in tweenData @@ -162,7 +162,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer anim.frameBounds = (new createjs.Rectangle(bounds...) for bounds in animData.frameBounds) anim.childMovieClips = @childMovieClips - + @spriteSheet.mcPool[key].push(anim) return anim @@ -256,10 +256,10 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer @baseMovieClip.specialGoToAndStop(translatedFrame) for movieClip in @childMovieClips movieClip.specialGoToAndStop(if movieClip.mode is 'single' then movieClip.startPosition else newFrame) - + @children = [] @takeChildrenFromMovieClip(@baseMovieClip, @) - + takeChildrenFromMovieClip: (movieClip, recipientContainer) -> for child in movieClip.childrenCopy if child instanceof createjs.MovieClip @@ -270,7 +270,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer recipientContainer.addChild(childRecipient) else recipientContainer.addChild(child) - + # _getBounds: createjs.SpriteContainer.prototype.getBounds # getBounds: -> @baseMovieClip?.getBounds() or @children[0]?.getBounds() or @_getBounds() diff --git a/app/lib/surface/SingularSprite.coffee b/app/lib/surface/SingularSprite.coffee index 9a7c93e2a..75127309c 100644 --- a/app/lib/surface/SingularSprite.coffee +++ b/app/lib/surface/SingularSprite.coffee @@ -1,31 +1,31 @@ SpriteBuilder = require 'lib/sprites/SpriteBuilder' -floors = ['Dungeon Floor', 'Indoor Floor', 'Grass', 'Grass01', 'Grass02', 'Grass03', 'Grass04', 'Grass05', 'Goal Trigger', 'Obstacle'] +floors = ['Dungeon Floor', 'Indoor Floor', 'Grass', 'Grass01', 'Grass02', 'Grass03', 'Grass04', 'Grass05', 'Goal Trigger', 'Obstacle', 'Sand 01', 'Sand 02', 'Sand 03', 'Sand 04', 'Sand 05', 'Sand 06', 'Talus 1', 'Talus 2', 'Talus 3', 'Talus 4', 'Talus 5', 'Talus 6', 'Firn 1', 'Firn 2', 'Firn 3', 'Firn 4', 'Firn 5', 'Firn 6', 'Ice Rink 1', 'Ice Rink 2', 'Ice Rink 3', 'Firn Cliff'] module.exports = class SingularSprite extends createjs.Sprite childMovieClips: null - + constructor: (@spriteSheet, @thangType, @spriteSheetPrefix, @resolutionFactor=SPRITE_RESOLUTION_FACTOR) -> super(@spriteSheet) - - destroy: -> + + destroy: -> @removeAllEventListeners() - + gotoAndPlay: (actionName) -> @goto(actionName, false) gotoAndStop: (actionName) -> @goto(actionName, true) _gotoAndPlay: createjs.Sprite.prototype.gotoAndPlay _gotoAndStop: createjs.Sprite.prototype.gotoAndStop - + goto: (actionName, @paused=true) -> @actionNotSupported = false - + action = @thangType.getActions()[actionName] - randomStart = actionName.startsWith('move') + randomStart = _.string.startsWith(actionName, 'move') reg = action.positions?.registration or @thangType.get('positions')?.registration or {x:0, y:0} if action.animation @framerate = (action.framerate ? 20) * (action.speed ? 1) - + func = if @paused then '_gotoAndStop' else '_gotoAndPlay' animationName = @spriteSheetPrefix + actionName @[func](animationName) @@ -65,14 +65,14 @@ module.exports = class SingularSprite extends createjs.Sprite @regY = -reg.y * scale @scaleX = @scaleY = 1 / @resolutionFactor - if @camera and @thangType.get('name') in floors - @baseScaleY *= @camera.y2x @scaleX *= -1 if action.flipX @scaleY *= -1 if action.flipY @baseScaleX = @scaleX @baseScaleY = @scaleY + if @camera and @thangType.get('name') in floors + @baseScaleY *= @camera.y2x @currentAnimation = actionName return - + notifyActionNeedsRender: (action) -> - @lank?.trigger('action-needs-render', @lank, action) \ No newline at end of file + @lank?.trigger('action-needs-render', @lank, action) diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index 341ef2d2e..35076ca6e 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -44,7 +44,6 @@ module.exports = Surface = class Surface extends CocoClass debug: false defaults: - wizards: true paths: true grid: false navigateToSelection: true @@ -113,7 +112,7 @@ module.exports = Surface = class Surface extends CocoClass canvasHeight = parseInt @normalCanvas.attr('height'), 10 @screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight - @lankBoss = new LankBoss camera: @camera, webGLStage: @webGLStage, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible + @lankBoss = new LankBoss camera: @camera, webGLStage: @webGLStage, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible, playerNames: @options.playerNames @countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer, showsCountdown: @world.showsCountdown @playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer @normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen. @@ -160,17 +159,11 @@ module.exports = Surface = class Surface extends CocoClass return if @loaded @loaded = true @lankBoss.createMarks() - @lankBoss.createIndieLanks @world.indieSprites, @options.wizards @updateState true @drawCurrentFrame() createjs.Ticker.addEventListener 'tick', @tick Backbone.Mediator.publish 'level:started', {} - createOpponentWizard: (opponent) -> - @lankBoss.createOpponentWizard opponent - - - #- Update loop tick: (e) => @@ -538,6 +531,7 @@ module.exports = Surface = class Surface extends CocoClass newHeight = newWidth / aspectRatio return unless newWidth > 0 and newHeight > 0 return if newWidth is oldWidth and newHeight is oldHeight and not @options.spectateGame + return if newWidth < 200 or newHeight < 200 #scaleFactor = if application.isIPadApp then 2 else 1 # Retina scaleFactor = 1 @normalCanvas.add(@webGLCanvas).attr width: newWidth * scaleFactor, height: newHeight * scaleFactor @@ -549,8 +543,9 @@ module.exports = Surface = class Surface extends CocoClass @normalStage.scaleY *= newHeight / oldHeight @camera.onResize newWidth, newHeight if @options.spectateGame - # Since normalCanvas is absolutely positioned, it needs help aligning with webGLCanvas. But not further than +149px (1920px screen). - @normalCanvas.css 'left', Math.min 149, @webGLCanvas.offset().left + # Since normalCanvas is absolutely positioned, it needs help aligning with webGLCanvas. + offset = @webGLCanvas.offset().left - ($('#page-container').innerWidth() - $('#canvas-wrapper').innerWidth()) / 2 + @normalCanvas.css 'left', offset #- Camera focus on hero focusOnHero: -> @@ -570,7 +565,6 @@ module.exports = Surface = class Surface extends CocoClass return if @realTime @realTime = true @onResize() - @lankBoss.selfWizardLank?.toggle false @playing = false # Will start when countdown is done. if @heroLank @previousCameraZoom = @camera.zoom @@ -581,7 +575,6 @@ module.exports = Surface = class Surface extends CocoClass @realTime = false @onResize() _.delay @onResize, resizeDelay + 100 # Do it again just to be double sure that we don't stay zoomed in due to timing problems. - @lankBoss.selfWizardLank?.toggle true @normalCanvas.add(@webGLCanvas).removeClass 'flag-color-selected' if @previousCameraZoom @camera.zoomTo @camera.newTarget or @camera.target, @previousCameraZoom, 3000 @@ -615,7 +608,7 @@ module.exports = Surface = class Surface extends CocoClass screenshot: (scale=0.25, format='image/jpeg', quality=0.8, zoom=2) -> # TODO: get screenshots working again # Quality doesn't work with image/png, just image/jpeg and image/webp - [w, h] = [@camera.canvasWidth, @camera.canvasHeight] + [w, h] = [@camera.canvasWidth * @camera.canvasScaleFactorX, @camera.canvasHeight * @camera.canvasScaleFactorY] margin = (1 - 1 / zoom) / 2 @webGLStage.cache margin * w, margin * h, w / zoom, h / zoom, scale * zoom imageData = @webGLStage.cacheCanvas.toDataURL(format, quality) diff --git a/app/lib/surface/WizardLank.coffee b/app/lib/surface/WizardLank.coffee deleted file mode 100644 index 3972a500f..000000000 --- a/app/lib/surface/WizardLank.coffee +++ /dev/null @@ -1,272 +0,0 @@ -IndieLank = require 'lib/surface/IndieLank' -{me} = require 'core/auth' - -module.exports = class WizardLank extends IndieLank - # Wizard targets are constantly changing, so a simple tween doesn't work. - # Instead, the wizard stores its origin point and the (possibly) moving target. - # Then it figures out its current position based on tween percentage and - # those two points. - tweenPercentage: 1.0 - originPos: null - targetPos: null - targetLank: null - reachedTarget: true - spriteXOffset: 4 # meters from target sprite - spriteYOffset: 0 # meters from target sprite - - subscriptions: - 'bus:player-states-changed': 'onPlayerStatesChanged' - 'auth:me-synced': 'onMeSynced' - 'surface:sprite-selected': 'onLankSelected' - 'sprite:echo-all-wizard-sprites': 'onEchoAllWizardLanks' - - shortcuts: - 'up': 'onMoveKey' - 'down': 'onMoveKey' - 'left': 'onMoveKey' - 'right': 'onMoveKey' - - constructor: (thangType, options) -> - if @isSelf = options.isSelf - options.colorConfig = $.extend(true, {}, me.get('wizard')?.colorConfig) or {} - super thangType, options - @targetPos = @thang.pos - if @isSelf - @setNameLabel me.displayName() - else if options.name - @setNameLabel options.name - Backbone.Mediator.publish 'self-wizard:created', sprite: @ - - makeIndieThang: (thangType, options) -> - thang = super thangType, options - thang.isSelectable = false - thang.bobHeight = 0.75 - thang.bobTime = 2 - thang.pos.z += thang.bobHeight - thang - - finishSetup: -> - @scaleFactor = @thang.scaleFactor if @thang?.scaleFactor - @updateScale() - @updateRotation() - # Don't call general update() because Thang isn't built yet - - setNameLabel: (name) -> - if @options.codeLanguage and @options.codeLanguage isnt 'javascript' and not @isSelf - name += " (#{@options.codeLanguage})" # TODO: move on second line, capitalize properly - super name - - toggle: (to) -> - @sprite?.visible = to - label[if to then 'show' else 'hide']() for name, label of @labels - mark.mark?.visible = to for name, mark of @marks - - onPlayerStatesChanged: (e) -> - for playerID, state of e.states - continue unless playerID is @thang.id - @setEditing state.wizard?.editing - continue if playerID is me.id # ignore changes for self wizard lank - @setNameLabel state.name - continue unless state.wizard? - if targetID = state.wizard.targetLank - return console.warn 'Wizard Lank couldn\'t find target lank', targetID unless targetID of @options.lanks - @setTarget @options.lanks[targetID] - else - @setTarget state.wizard.targetPos - - onMeSynced: (e) -> - return unless @isSelf - @setNameLabel me.displayName() if @sprite.visible # not if we hid the wiz - newColorConfig = me.get('wizard')?.colorConfig or {} - shouldUpdate = not _.isEqual(newColorConfig, @options.colorConfig) - @options.colorConfig = $.extend(true, {}, newColorConfig) - if shouldUpdate - @playAction(@currentAction) if @currentAction - - onLankSelected: (e) -> - return unless @isSelf - @setTarget e.sprite or e.worldPos - - animateIn: -> - @sprite.scaleX = @sprite.scaleY = @sprite.alpha = 0 - createjs.Tween.get(@sprite) - .to({scaleX: 1, scaleY: 1, alpha: 1}, 1000, createjs.Ease.getPowInOut(2.2)) - @labels.name?.show() - - animateOut: (callback) -> - tween = createjs.Tween.get(@sprite) - .to({scaleX: 0, scaleY: 0, alpha: 0}, 1000, createjs.Ease.getPowInOut(2.2)) - tween.call(callback) if callback - @labels.name?.hide() - - setEditing: (@editing) -> - if @editing - @thang.actionActivated = @thang.action isnt 'cast' - @thang.action = 'cast' - else - @thang.action = 'idle' if @thang.action is 'cast' - - setInitialState: (targetPos, @targetLank) -> - @targetPos = @getPosFromTarget(@targetLank or targetPos) - @endMoveTween() - - onEchoAllWizardLanks: (e) -> e.payload.push @ - defaultPos: -> x: 35, y: 24, z: @thang.depth / 2 + @thang.bobHeight - move: (pos, duration) -> @setTarget(pos, duration) - - setTarget: (newTarget, duration, isLinear=false) -> - # ignore targets you're already heading for - targetPos = @getPosFromTarget(newTarget) - return if @targetPos and @targetPos.x is targetPos.x and @targetPos.y is targetPos.y - - # ignore selecting sprites you can't control - isLank = newTarget?.thang? - return if isLank and not newTarget.thang.isProgrammable - return if isLank and newTarget is @targetLank - - @shoveOtherWizards(true) if @targetLank - @targetLank = if isLank then newTarget else null - @targetPos = @boundWizard targetPos - @beginMoveTween(duration, isLinear) - @shoveOtherWizards() - Backbone.Mediator.publish('self-wizard:target-changed', {sprite: @}) if @isSelf - - boundWizard: (target) -> - # Passed an {x, y} in world coordinates, returns {x, y} within world bounds - return target unless @options.camera.bounds - @bounds = @options.camera.bounds - surfaceTarget = @options.camera.worldToSurface target - x = Math.min(Math.max(surfaceTarget.x, @bounds.x), @bounds.x + @bounds.width) - y = Math.min(Math.max(surfaceTarget.y, @bounds.y), @bounds.y + @bounds.height) - return @options.camera.surfaceToWorld {x: x, y: y} - - getPosFromTarget: (target) -> - """ - Could be null, a vector, or sprite object. Get the position from any of these. - """ - return @defaultPos() unless target? - return target if target.x? - return target.thang.pos - - beginMoveTween: (duration=1000, isLinear=false) -> - # clear the old tween - createjs.Tween.removeTweens(@) - - # create a new tween to go from the current location to the new location - @originPos = _.clone(@thang.pos) - @tweenPercentage = 1.0 - @thang.action = 'move' - @pointToward(@targetPos) - if duration is 0 - @updatePosition() - @endMoveTween() - return - if isLinear - ease = createjs.Ease.linear - else - ease = createjs.Ease.getPowInOut(3.0) - - createjs.Tween - .get(@) - .to({tweenPercentage: 0.0}, duration, ease) - .call(@endMoveTween) - @reachedTarget = false - @update true - - shoveOtherWizards: (removeMe) -> - return unless @targetLank - allWizards = [] - Backbone.Mediator.publish 'sprite:echo-all-wizard-sprites', payload: allWizards - allOfUs = (wizard for wizard in allWizards when wizard.targetLank is @targetLank) - allOfUs = (wizard for wizard in allOfUs when wizard isnt @) if removeMe - - # diagonal lineup pattern -# wizardPosition = [[4, 0], [5,1], [3,-1], [6,2], [2,-2]] -# step = 3 -# for wizard, i in allOfUs -# [x,y] = wizardPositions[i%@wizardPositions.length] -# wizard.spriteXOffset = x -# wizard.spriteYOffset = y -# wizard.beginMoveTween() - - # circular pattern - step = Math.PI * 2 / allOfUs.length - for wizard, i in allOfUs - wizard.spriteXOffset = 5*Math.cos(step*i) - wizard.spriteYOffset = 4*Math.sin(step*i) - wizard.beginMoveTween() - - endMoveTween: => - return if @destroyed - @thang.action = if @editing then 'cast' else 'idle' - @thang.actionActivated = @thang.action is 'cast' - @reachedTarget = true - @faceTarget() - @update true - - updatePosition: (whileLoading=false) -> - return if whileLoading or not @options.camera - @thang.pos = @getCurrentPosition() - @faceTarget() - sup = @options.camera.worldToSurface x: @thang.pos.x, y: @thang.pos.y, z: @thang.pos.z - @thang.depth / 2 - @sprite.x = sup.x - @sprite.y = sup.y - - getCurrentPosition: -> - """ - Takes into account whether the wizard is in transit or not, and the bobbing up and down. - Eventually will also adjust based on where other wizards are. - """ - @targetPos = @targetLank.thang.pos if @targetLank?.thang - pos = _.clone(@targetPos) - pos.z = @defaultPos().z + @getBobOffset() - @adjustPositionToSideOfTarget(pos) if @targetLank # be off to the side depending on placement in world - return pos if @reachedTarget # stick like glue - - # if here, then the wizard is in transit. Calculate the diff! - pos = - x: pos.x + ((@originPos.x - pos.x) * @tweenPercentage) - y: pos.y + ((@originPos.y - pos.y) * @tweenPercentage) - z: pos.z - return pos - - adjustPositionToSideOfTarget: (targetPos) -> - targetPos.x += @spriteXOffset - return - # doesn't work when you're zoomed in on the target, so disabling - center = @options.camera.surfaceToWorld(@options.camera.currentTarget).x - distanceFromCenter = Math.abs(targetPos.x - center) - if @spriteXOffset - distanceFromTarget = Math.abs(@spriteXOffset) - (1 / (distanceFromCenter + (1/Math.abs(@spriteXOffset)))) - else - distanceFromTarget = 0 - @onLeftSide = targetPos.x > center - @onLeftSide = not @onLeftSide if @spriteXOffset < 0 - distanceFromTarget *= -1 if @onLeftSide - targetPos.x += distanceFromTarget # adjusted - targetPos.y += @spriteYOffset - - faceTarget: -> - if @targetLank?.thang - @pointToward(@targetLank.thang.pos) - - updateMarks: -> - super() if @sprite.visible # not if we hid the wiz - - onMoveKey: (e) -> - return unless @isSelf - e?.preventDefault() - yMovement = 0 - xMovement = 0 - yMovement += 2 if key.isPressed('up') - yMovement -= 2 if key.isPressed('down') - xMovement += 2 if key.isPressed('right') - xMovement -= 2 if key.isPressed('left') - @moveWizard xMovement, yMovement - - moveWizard: (x, y) -> - interval = 500 - position = {x: @targetPos.x + x, y: @targetPos.y + y} - @setTarget(position, interval, true) - @updatePosition() - Backbone.Mediator.publish 'camera:zoom-to', pos: position, duration: interval diff --git a/app/lib/world/GoalManager.coffee b/app/lib/world/GoalManager.coffee index a9c933221..abef73901 100644 --- a/app/lib/world/GoalManager.coffee +++ b/app/lib/world/GoalManager.coffee @@ -278,7 +278,7 @@ module.exports = class GoalManager extends CocoClass # saveThangs: by default we would want to save all the Thangs, which means that we would want none of them to be 'done' numNeeded = _.size(stateThangs) - Math.max((goal.howMany ? 1), _.size stateThangs) + 1 numDone = _.filter(stateThangs).length - #console.log 'needed', numNeeded, 'done', numDone, 'of total', _.size(stateThangs), 'with how many', goal.howMany, 'and stateThangs', stateThangs, 'for', goalID, thangID, 'on frame', frameNumber + #console.log 'needed', numNeeded, 'done', numDone, 'of total', _.size(stateThangs), 'with how many', goal.howMany, 'and stateThangs', stateThangs, 'for', goalID, thangID, 'on frame', frameNumber, 'all Thangs', _.keys(stateThangs), _.values(stateThangs) return unless numDone >= numNeeded return if state.status and not success # already failed it; don't wipe keyframe state.status = if success then 'success' else 'failure' diff --git a/app/lib/world/Grid.coffee b/app/lib/world/Grid.coffee index edd383bfc..a062d8bbe 100644 --- a/app/lib/world/Grid.coffee +++ b/app/lib/world/Grid.coffee @@ -1,7 +1,7 @@ # TODO: this thing needs a bit of thinking/testing for grid square alignments, exclusive vs. inclusive mins/maxes, etc. module.exports = class Grid - constructor: (thangs, @width, @height, @padding=0, @left=0, @bottom=0) -> + constructor: (thangs, @width, @height, @padding=0, @left=0, @bottom=0, @rogue=false) -> @width = Math.ceil @width @height = Math.ceil @height @left = Math.floor @left @@ -14,7 +14,11 @@ module.exports = class Grid @grid.push [] for x in [0 .. @width] @grid[y].push [] - for thang in thangs when thang.collides + if @rogue + thangs = (t for t in thangs when t.collides or t.spriteName is 'Gem' and not t.dead) + else + thangs = (t for t in thangs when t.collides) + for thang in thangs rect = thang.rectangle() [minX, maxX, minY, maxY] = [9001, -9001, 9001, -9001] for v in rect.vertices() @@ -48,7 +52,24 @@ module.exports = class Grid rows: (minX, maxX) -> [@clampRow(minX) ... @clampRow(maxX)] - toString: -> + toString: (rogue=false) -> upsideDown = _.clone @grid upsideDown.reverse() - (((if thangs.length then ('' + thangs.length) else ' ') for thangs in row).join(' ') for row in upsideDown).join("\n") + ((@charForThangs thangs, rogue for thangs in row).join(' ') for row in upsideDown).join("\n") + + charForThangs: (thangs, rogue) -> + return thangs.length or ' ' unless rogue + return '.' unless thangs.length + return '@' if _.find thangs, (t) -> /Hero Placeholder/.test t.id + return '>' if _.find thangs, spriteName: 'Spike Walls' + return 'F' if _.find thangs, spriteName: 'Fence Wall' + return 'T' if _.find thangs, spriteName: 'Fire Trap' + return ' ' if _.find thangs, spriteName: 'Dungeon Wall' + return 'G' if _.find thangs, spriteName: 'Gem' + return 'C' if _.find thangs, spriteName: 'Treasure Chest' + return '*' if _.find thangs, spriteName: 'Spear' + return 'o' if _.find thangs, type: 'munchkin' + return 'O' if _.find thangs, (t) -> t.team is 'ogres' + return 'H' if _.find thangs, (t) -> t.team is 'humans' + return 'N' if _.find thangs, (t) -> t.team is 'neutral' + return '?' diff --git a/app/lib/world/ellipse.coffee b/app/lib/world/ellipse.coffee index 6aaf88057..e940ce8d4 100644 --- a/app/lib/world/ellipse.coffee +++ b/app/lib/world/ellipse.coffee @@ -50,50 +50,18 @@ class Ellipse if shape.isEllipse then @distanceSquaredToEllipse shape else @distanceSquaredToRectangle shape containsPoint: (p, withRotation=true) -> - [a, b] = [@width / 2, @height / 2] - [h, k] = [@x, @y] - [x, y] = [p.x, p.y] - x2 = Math.pow(x, 2) - a2 = Math.pow(a, 2) - a4 = Math.pow(a, 4) - b2 = Math.pow(b, 2) - b4 = Math.pow(b, 4) - h2 = Math.pow(h, 2) - k2 = Math.pow(k, 2) - if withRotation and @rotation - sint = Math.sin(@rotation) - sin2t = Math.sin(2 * @rotation) - cost = Math.cos(@rotation) - cos2t = Math.cos(2 * @rotation) - numeratorLeft = (-a2 * h * sin2t) + (a2 * k * cos2t) + (a2 * k) + (a2 * x * sin2t) - numeratorMiddle = Math.SQRT2 * Math.sqrt((a4 * b2 * cos2t) + (a4 * b2) - (a2 * b4 * cos2t) + (a2 * b4) - (2 * a2 * b2 * h2) + (4 * a2 * b2 * h * x) - (2 * a2 * b2 * x2)) - numeratorRight = (b2 * h * sin2t) - (b2 * k * cos2t) + (b2 * k) - (b2 * x * sin2t) - denominator = (a2 * cos2t) + a2 - (b2 * cos2t) + b2 - solution1 = (numeratorLeft - numeratorMiddle + numeratorRight) / denominator - solution2 = (numeratorLeft + numeratorMiddle + numeratorRight) / denominator - if (not isNaN solution1) and (not isNaN solution2) - [bigSolution, littleSolution] = if solution1 > solution2 then [solution1, solution2] else [solution2, solution1] - if y > littleSolution and y < bigSolution - return true - else - return false - else - return false - else - numeratorLeft = a2 * k - numeratorRight = Math.sqrt((a4 * b2) - (a2 * b2 * h2) + (2 * a2 * b2 * h * x) - (a2 * b2 * x2)) - denominator = a2 - solution1 = (numeratorLeft + numeratorRight) / denominator - solution2 = (numeratorLeft - numeratorRight) / denominator - if (not isNaN solution1) and (not isNaN solution2) - [bigSolution, littleSolution] = if solution1 > solution2 then [solution1, solution2] else [solution2, solution1] - if y > littleSolution and y < bigSolution - return true - else - return false - else - return false - false + # "ellipse space" is the cartesian space + # where the ellipse becomes the unit + # circle centered at (0, 0) + [x, y] = [p.x - @x, p.y - @y] # translate point into ellipse space + if withRotation and @rotation # optionally rotate point into ellipse space + c = Math.cos(@rotation) + s = Math.sin(@rotation) + [x, y] = [x*c + y*s, y*c - x*s] + x = x / @width * 2 # scale point into ellipse space + y = y / @height * 2 + x*x + y*y <= 1 #if the resulting point falls on/in the unit circle at 0, 0 + intersectsLineSegment: (p1, p2) -> [px1, py1, px2, py2] = [p1.x, p1.y, p2.x, p2.y] @@ -153,7 +121,7 @@ class Ellipse rectangle.intersectsEllipse @ intersectsEllipse: (ellipse) -> - @rectangle().intersectsEllipse @ # TODO: actually implement ellipse-ellipse intersection + @rectangle().intersectsEllipse ellipse # TODO: actually implement ellipse-ellipse intersection #return true if @containsPoint ellipse.getPos() intersectsShape: (shape) -> diff --git a/app/lib/world/errors.coffee b/app/lib/world/errors.coffee index c4eee90e7..c766d76a6 100644 --- a/app/lib/world/errors.coffee +++ b/app/lib/world/errors.coffee @@ -3,7 +3,7 @@ Vector = require './vector' module.exports.ArgumentError = class ArgumentError extends Error @className: 'ArgumentError' constructor: (@message, @functionName, @argumentName, @intendedType, @actualValue, @numArguments, @hint) -> - super message + super @message @name = 'ArgumentError' if Error.captureStackTrace? Error.captureStackTrace @, @constructor diff --git a/app/lib/world/names.coffee b/app/lib/world/names.coffee index 08f575801..08f2863e6 100644 --- a/app/lib/world/names.coffee +++ b/app/lib/world/names.coffee @@ -1,416 +1,966 @@ module.exports.thangNames = thangNames = - 'Soldier M': [ - 'Duke' - 'William' - 'Lucas' - 'Marcus' - 'Robert' - 'Gordon' - 'Kirin' - 'Theo' - 'Roger' - 'Roderick' - 'Samson' - 'Silas' - 'Richard' - 'Max' - 'Jax' - 'Dax' - 'Mischa' - 'Ronald' - 'Tyrone' - 'Thelonious' - 'Miles' - 'Bill' - 'Kumar' - 'Ricardo' - 'Maxwell' - 'Jonah' - 'Leopold' - 'Phineas' - 'Ferb' - 'Felix' - 'Ezra' - 'Lucian' - 'Augustus' - 'Ronan' - 'Pierce' - 'Harry' - 'Hirium' - 'Hugo' - 'Cecil' - 'Barron' - 'Huburt' - 'Sterling' - 'Alistair' - 'Cid' - 'Remy' - 'Stormy' - 'Halle' - 'Sage' - 'Ryan' - 'Bond' - 'Philippian' - ] - 'Soldier F': [ - 'Sarah' - 'Alexandra' - 'Holly' - 'Trinity' - 'Nikita' - 'Alana' - 'Lana' - 'Joan' - 'Helga' - 'Annie' - 'Lukaz' - 'Gorgin' - 'Coco' - 'Buffy' - 'Allankrita' - 'Kay' - 'Shannon' - 'Scarlett' - 'Natasha' - 'Aphrodite' - 'Gabrielle' - 'Imani' - ] - 'Peasant M': [ - 'Yorik' - 'Hector' - 'Thad' - 'Victor' - 'Lyle' - 'Charles' - 'Yusef' - 'Hingle' - 'Azgot' - 'Piers' - 'Carlton' - 'Hershell' - 'Gawain' - 'Durfkor' - 'Paps' - 'Hodor' - 'James' - 'Merek' - 'Brom' - 'Tybalt' - 'Fendrel' - ] - 'Peasant F': [ - 'Hilda' - 'Icey' - 'Matilda' - 'Mertia' - 'Mary' - 'Brandy' - 'Gwendolin' - 'Tabitha' - 'Regan' - 'Giselle' - 'Bernadette' - 'Millicent' - 'Anastas' - 'Thea' - 'Ellyn' - 'Alianor' - 'Anastas' - 'Cristiana' - 'Helena' - 'Alexia' - 'Katelyn' - 'Rose' - - ] - 'Archer F': [ - 'Phoebe' - 'Mira' - 'Agapi' - 'Cecily' - 'Tansy' - 'Ivy' - 'Gemma' - 'Keturah' - 'Korra' - 'Kim' - 'Odette' - 'Orly' - 'Mercedes' - 'Rosaline' - 'Vesper' - 'Beverly' - 'Natalie' - 'Clare' - 'Rowan' - 'Omar' - 'Alden' - 'Cairn' - 'Jensen' - 'Yilitha' - 'Mirana' - 'Lina' - 'Luna' - 'Alleria' - 'Vereesa' - 'Beatrice' - ] - 'Archer M': [ - 'Brian' - 'Cole' - 'Roman' - 'Hunter' - 'Simon' - 'Robin' - 'Quinn' - 'Arty' - 'Gimsley' - 'Fidsdale' - 'Slyvos' - 'Logos' - 'Denin' - 'Lycan' - 'Loco' - 'Vican' - 'Mars' - 'Dev' - 'Oliver' + 'Ogre Munchkin F': [ + # Female + 'Alali' + 'Anabel' + 'Dosha' + 'Gurzunn' + 'Hoot' + 'Inski' + 'Iyert' + 'Lacos' + 'Palt' + 'Paulark' + 'Pripp' + 'Shmeal' + 'Upfish' + 'Yugark' ] 'Ogre Munchkin M': [ + # Male + 'Blob' 'Brack' - 'Gort' - 'Weeb' - 'Nerph' - 'Kratt' - 'Smerk' - 'Raack' 'Dobo' 'Draff' - 'Zozo' - 'Kogpole' - 'Leerer' - 'Skoggen' - 'Treg' - 'Goreball' + 'Eugen' 'Gert' - 'Thabt' - 'Snortt' + 'Godel' + 'Goreball' + 'Gordok' + 'Gorylo' + 'Gort' 'Kog' - 'Ursa' + 'Kogpole' + 'Kratt' + 'Leerer' + 'Nerph' + 'Oogre' + 'Raack' 'Ragtime' - 'Blob' - ] - 'Ogre Munchkin F': [ - 'Iyert' - 'Palt' - 'Shmeal' - 'Gurzunn' - 'Yugark' - 'Dosha' - 'Inski' - 'Lacos' - 'Upfish' - 'Hoot' - ] - 'Ogre Peon M': [ - 'Durbo' - 'Kurger' - 'Mudwich' - 'Ba Bo' - 'Zugger' - 'Toe Pod' - ] - 'Ogre Peon F': [ - 'Iblet' - 'Lorba' - 'Zzoya' - 'Yamra' - 'Greeke' - 'Vapa' - ] - 'Ogre M': [ - 'Krogg' - 'Dronck' - 'Trogdor' - 'Kulgor' - 'Skrungt' - 'Mak Fod' - 'Trung' - 'Axe Ox' - 'Vargutt' - 'Grumus' - 'Gug' - 'Tarlok' - 'Gurulax' - 'Mokrul' - 'Polifemo' - 'Muthyala' - 'Saltporker' - ] - 'Ogre F': [ - 'Nareng' - 'Morthrug' - 'Glonc' - 'Marghurk' - 'Martha' - 'Holkam' - 'Alkaz' - 'Gar\'ah' - 'Mak\'rah' - 'Marnag' - ] - 'Ogre Brawler': [ - 'Grul\'thock' - 'Boz' - 'Trod' - 'Muul' - 'Grumoll' - 'Burobb' - 'Arelt' - 'Zagurk' - 'Zeredd' - 'Borgag' - 'Grognar' - 'Ironjaw' - 'Tuguro' - 'York' - 'Ork\'han' - 'Roast Beefy' - 'Haggar' - ] - 'Ogre Fangrider': [ - 'Dreek' - 'Flarsho' - 'Mizzy' - 'Secka' - 'Arizard' - 'Morzgret' - 'Doralt' - 'Geggret' - 'Gurzthrot' - 'Murgark' - 'Muttin' - 'Bortrok' - ] - 'Ogre Shaman': [ - 'Sham\'uk' - 'Il\'Du\'duka' - 'Ahst\'durante' - 'Poult' - 'Aolian\'Tak' - 'Tuzell' - 'Yamizeb' - 'Yerong' - 'Tuzang' - 'Varreth' - 'Yugargen' - 'Turann' - 'Ugoki' - 'Zulabar' - 'Zo\'Goroth' - 'Mogadishu' - 'Nazgareth' - 'Gror' - 'Grek' - 'Gom' - 'Gogg' - 'Ghuk' - 'Makas' - 'Drun' + 'Raort' + 'Rexxar' + 'Skoggen' + 'Smerk' + 'Snortt' + 'Thabt' + 'Toremon' + 'Treg' + 'Ursa' + 'Vorobun' + 'Weeb' + 'Yart' + 'Zozo' ] 'Ogre Thrower': [ - 'Kyrgg' + # Female + 'Beebatha' + 'Dross' + 'Drumbaa' 'Durnath' - 'Kraggan' - 'Rasha' - 'Moza' - 'Vujii' 'Esha' - 'Zara' + 'Gragthar' + 'Grel' 'Hamedi' 'Jinjin' - 'Yetu' + 'Kraggan' + 'Kyrgg' 'Makas' - 'Rakash' - 'Drumbaa' + 'Moza' 'Pinakin' + 'Rakash' + 'Rasha' + 'Vujii' + 'Wuda' + 'Yetu' + 'Zara' + ] + 'Griffin Rider': [ + # Female + 'Aeoldan' + 'Bestarius' + 'Cristofide' + 'Denestorath' + 'Letholdus' + 'Loretha' + ] + 'Paladin': [ + # Female + 'Illumina' + 'Celadia' + 'Taric' + 'Vaelia' + 'Antary' + ] + 'Ogre Witch': [ + # Female + 'Vyrryx' + 'Yzzrith' + ] + 'Ogre Chieftain': [ + # Female + 'Zagra Ux' + 'Oniko' + ] + 'Ogre Warlock': [ + # Male + 'Vax' + 'Vyrryx' + 'Vyjj' + ] + 'Ogre Scout M': [ + # Male + 'Frandar' + 'Karnaugh' + 'Lanthon' + 'Tarjan' + 'Yorgalfen' + ] + 'Ogre Scout F': [ + # Female + 'Freesa' + 'Ganju' + 'Hopper' + 'Ralthora' ] 'Burl': [ + # Animal 'Borlit' 'Burlosh' 'Dorf' ] - 'Griffin Rider': [ - 'Aeoldan' - 'Bestarius' - 'Letholdus' + 'Sand Yak': [ + # Animal + 'Arngotho' + 'Falthror' + 'Girvan' + 'Langthok' + 'Ofgar' + 'Randall' + ] + 'Raven': [ + # Animal + 'Nevermore' + 'Baltimore' + ] + 'Cougar': [ + # Animal + 'Guenhwyvar' + 'Kitty' + 'Shasta' + 'Simbia' + ] + 'Frog': [ + # Animal + 'Bighead' + 'Hypnotoad' + 'Freddy' + 'Frogger' + 'Froggy' + 'Slippy' + 'Wart' + 'Bufo' + 'Bunda' + 'Dan\'l Webster' + 'Mr. Toad' + 'Trevor' + 'Wei Qi' + 'Toada' + ] + 'Horse': [ + # Animal + 'Abby' + 'Wildsilver' + 'Fleetfire' + 'Ed' + 'Silver' + 'Hurricane' + 'Beauty' + 'Lovelace' + 'Mirial' + 'Miracle' + 'Codasus' + ] + 'Ogre M': [ + # Male + 'Axe Ox' + 'Belch' + 'Booz' + 'Brusentsov' + 'Demonik' + 'Dronck' + 'Gorlog' + 'Grumus' + 'Gug' + 'Gurulax' + 'Krogg' + 'Kulgor' + 'Mak Fod' + 'Mokrul' + 'Muthyala' + 'Oni' + 'Polifemo' + 'Saltporker' + 'Skrungt' + 'Steve' + 'Stinker' + 'Tarlok' + 'Trogdor' + 'Trung' + 'Vargutt' + 'Vyle' + ] + 'Ogre F': [ + # Female + 'Alkaz' + 'Gar\'ah' + 'Glonc' + 'Holkam' + 'Kriskull' + 'Mak\'rah' + 'Marghurk' + 'Marnag' + 'Martha' + 'Morthrug' + 'Nareng' + 'Maleda' + ] + 'Ogre Brawler': [ + # Male + 'Arelt' + 'Borgag' + 'Boz' + 'Burobb' + 'Dijkstro' + 'Grognar' + 'Grul\'thock' + 'Grumoll' + 'Haggar' + 'Heizenburg' + 'Ironjaw' + 'Muul' + 'Ork\'han' + 'Roast Beefy' + 'Trod' + 'Tuguro' + 'York' + 'Zagurk' + 'Zeredd' + ] + 'Ogre Fangrider': [ + # Female + 'Arizard' + 'Bortrok' + 'Boruvka' + 'Doralt' + 'Dreek' + 'Flarsho' + 'Geggret' + 'Gurzthrot' + 'Mizzy' + 'Morzgret' + 'Murgark' + 'Muttin' + 'Secka' + ] + 'Ogre Shaman': [ + # Female + 'Ahst\'durante' + 'Aolian\'Tak' + 'Drun' + 'Ghuk' + 'Gogg' + 'Gom' + 'Grek' + 'Gror' + 'Il\'Du\'duka' + 'Makas' + 'Mogadishu' + 'Nazgareth' + 'Poult' + 'Sham\'uk' + 'Torluk' + 'Turann' + 'Tuzang' + 'Tuzell' + 'Ugoki' + 'Uld\'Mak' + 'Varreth' + 'Yamizeb' + 'Yerong' + 'Yugargen' + 'Zo\'Goroth' + 'Zulabar' + ] + 'Skeleton': [ + # Both + 'Bloody Johnny' + 'Bone Daddy' + 'Bonejangles' + 'Bonesworth' + 'Bonette' + 'Doornail' + 'Drybones' + 'Grim' + 'Haskell' + 'Indiana Bones' + 'James Bone' + 'Kate' + 'Palatine' + 'Ribster' + 'Rusty' + 'Sacra' + 'Scraps' + 'Shelly' + 'Shishka-Bob' + 'Shishka-Larry' + 'Shishka-Joe' + 'Skeletor' + 'Skellington' + 'Skulldugger' + 'Skully' + 'Smitty' + 'Sphenoid' + 'Sternum' + 'Talus' + 'Tatava' + 'Ulna' + 'Yorick' + 'Boneus' + ] + 'Ogre Headhunter': [ + # Male + 'Bob' + 'Deadtooth' + 'Ez the Cruel' + 'Grroq' + 'Mog' + 'Mogvar' + 'Ral\'thuk' + 'Soth' + 'Ulxx' + 'Ur' + 'Veznyr' + 'Warlegs' + 'Xul Gor' + ] + 'Trapper': [ + # Male + 'Senick' + 'John' + 'Kyle' + ] + 'Forest Archer': [ + # Female + 'Naria' + 'Sylva' + 'Archia' + ] + 'Raider': [ + # Female + 'Arryn' + 'Thrat' + ] + 'Goliath': [ + # Male + 'Okar' + ] + 'Guardian': [ + # Female + 'Illia' + 'Gaia' + ] + 'Pixie': [ + # Female + 'Zana' + 'Eika' + ] + 'Assassin': [ + # Male + 'Blackjack' + 'Kha\'Zix' + 'Ritic' + 'Rengar' + 'Shade' + 'Talon' + 'Zed' + 'Sherkey' + ] + 'Necromancer': [ + # Male + 'Krekai' + 'Kethum' + 'Morcelu' + 'Nalfar' + 'Drezhul' + ] + 'Dark Wizard': [ + # Female + 'Lilith' + 'Usara' + 'Veigar' + 'Voldemort' + ] + 'Archer F': [ + # Female + 'Agapi' + 'Alden' + 'Alleria' + 'Atalanta' + 'Artemis' + 'Beatrice' + 'Bachi' + 'Beverly' + 'Cairn' + 'Cecily' + 'Clare' + 'Erica' + 'Gemma' + 'Ivy' + 'Jensen' + 'Katniss' + 'Katreena' + 'Keturah' + 'Kim' + 'Korra' + 'Lina' + 'Luna' + 'Mercedes' + 'Mira' + 'Mirana' + 'Natalie' + 'Odette' + 'Omar' + 'Orly' + 'Phoebe' + 'Prim' + 'Rosaline' + 'Rowan' + 'Tansy' + 'Tauriel' + 'Vereesa' + 'Vesper' + 'Yilitha' + ] + 'Archer M': [ + # Male + 'Arty' + 'Brian' + 'Cole' + 'Denin' + 'Dev' + 'Fidsdale' + 'Gimsley' + 'Hunter' + 'Kikariy' + 'Legolas' + 'Loco' + 'Logos' + 'Lycan' + 'Mars' + 'Odysseos' + 'Oliver' + 'Quinn' + 'Robin' + 'Roman' + 'Simon' + 'Slyvos' + 'Vican' + 'Warshall' + 'Yue Fei' + 'Zhou Tong' + ] + 'Peasant M': [ + # Male + 'Azgot' + 'Brom' + 'Carlton' + 'Charles' + 'Durfkor' + 'Duan' + 'Fendrel' + 'Gawain' + 'Hamming' + 'Hector' + 'Hershell' + 'Hingle' + 'Hodor' + 'Jackson' + 'James' + 'Lyle' + 'Merek' + 'Paps' + 'Piers' + 'Shimron' + 'Thad' + 'Tybalt' + 'Victor' + 'Winkler' + 'Yorik' + 'Yusef' + ] + 'Peasant F': [ + # Female + 'Alexia' + 'Alianor' + 'Anastas' + 'Bernadette' + 'Brandy' + 'Cristiana' + 'Ellyn' + 'Giselle' + 'Gwendolin' + 'Helena' + 'Hilda' + 'Icey' + 'Katelyn' + 'Mary' + 'Matilda' + 'Mertia' + 'Millicent' + 'Regan' + 'Rose' + 'Ruth' + 'Tabitha' + 'Thea' + ] + 'Soldier M': [ + # Male + 'Aaron' + 'Adam' + 'Addison' + 'Alan' + 'Albert' + 'Alistair' + 'Andrew' + 'Anthony' + 'Antonio' + 'Arthur' + 'Augustus' + 'Barron' + 'Benjamin' + 'Bill' + 'Billy' + 'Bobby' + 'Bond' + 'Brandon' + 'Brian' + 'Bruce' + 'Carl' + 'Carlos' + 'Cecil' + 'Charles' + 'Chris' + 'Christopher' + 'Cid' + 'Clarence' + 'Craig' + 'Daniel' + 'Darius' + 'David' + 'Dax' + 'Dennis' + 'Donald' + 'Douglas' + 'Duke' + 'Earl' + 'Edward' + 'Edwin' + 'Eric' + 'Ernest' + 'Eugene' + 'Ezra' + 'Felix' + 'Ferb' + 'Frank' + 'Fred' + 'Gary' + 'Gatsby' + 'George' + 'Gerald' + 'Gordon' + 'Gregory' + 'Guan Yu' + 'Halle' + 'Harold' + 'Harry' + 'Henry' + 'Hirium' + 'Howard' + 'Huburt' + 'Hugo' + 'Ieyasu' + 'Jack' + 'James' + 'Jason' + 'Jax' + 'Jeffrey' + 'Jeremy' + 'Jerry' + 'Jesse' + 'Jimmy' + 'Joe' + 'John' + 'Johnny' + 'Jonah' + 'Jonathan' + 'Jose' + 'Joseph' + 'Joshua' + 'Juan' + 'Jun Fan' + 'Justin' + 'Keith' + 'Kenneth' + 'Kevin' + 'Kirin' + 'Kumar' + 'Larry' + 'Lawrence' + 'Leopold' + 'Louis' + 'Lucas' + 'Lucian' + 'Malcolm' + 'Marcus' + 'Mark' + 'Martin' + 'Matthew' + 'Max' + 'Maxwell' + 'Michael' + 'Miles' + 'Mischa' + 'Musashi' + 'Nicholas' + 'Nick' + 'Noah' + 'Orion' + 'Parker' + 'Patrick' + 'Paul' + 'Peter' + 'Philip' + 'Philippian' + 'Phillip' + 'Phineas' + 'Pierce' + 'Ralph' + 'Randy' + 'Raymond' + 'Remy' + 'Ricardo' + 'Richard' + 'Robert' + 'Roderick' + 'Roger' + 'Ronald' + 'Ronan' + 'Roy' + 'Russell' + 'Ryan' + 'Sage' + 'Samson' + 'Samuel' + 'Scott' + 'Sean' + 'Shawn' + 'Silas' + 'Stephen' + 'Sterling' + 'Steve' + 'Steven' + 'Stormy' + 'Tadakatsu' + 'Terry' + 'Thelonious' + 'Theo' + 'Thomas' + 'Timothy' + 'Todd' + 'Tryndamere' + 'Tyrone' + 'Victor' + 'Walter' + 'Wayne' + 'William' + 'Willie' + 'Zachary' + ] + 'Soldier F': [ + # Female + 'Ahri' + 'Alana' + 'Alexandra' + 'Alice' + 'Allankrita' + 'Amanda' + 'Amy' + 'Andrea' + 'Angela' + 'Ann' + 'Anna' + 'Anne' + 'Annie' + 'Ann-Maria' + 'Aphrodite' + 'Ashley' + 'Barbara' + 'Betty' + 'Beverly' + 'Bonnie' + 'Brenda' + 'Buffy' + 'Carol' + 'Carolyn' + 'Catherine' + 'Cheryl' + 'Christina' + 'Christine' + 'Coco' + 'Cynthia' + 'Deborah' + 'Debra' + 'Denise' + 'Diana' + 'Diane' + 'Donna' + 'Doris' + 'Dorothy' + 'Elizabeth' + 'Emma' + 'Emily' + 'Evelyn' + 'Fiora' + 'Frances' + 'Gabrielle' + 'Gloria' + 'Gorgin' + 'Heather' + 'Helen' + 'Helga' + 'Holly' + 'Imani' + 'Irene' + 'Jacqueline' + 'Jane' + 'Janet' + 'Janice' + 'Jean' + 'Jennifer' + 'Jessica' + 'Joan' + 'Jordan' + 'Joyce' + 'Judith' + 'Judy' + 'Julia' + 'Julie' + 'Karen' + 'Katherine' + 'Kathleen' + 'Kathryn' + 'Kathy' + 'Kay' + 'Kelly' + 'Kimberly' + 'Kira' + 'Lana' + 'Laura' + 'Lillian' + 'Linda' + 'Lisa' + 'Lois' + 'Lori' + 'Louise' + 'Lukaz' + 'Margaret' + 'Maria' + 'Mariah' + 'Marie' + 'Marilyn' + 'Martha' + 'Mary' + 'Medusa' + 'Melissa' + 'Michelle' + 'Mildred' + 'Mulan' + 'Nancy' + 'Natasha' + 'Nicole' + 'Nikita' + 'Norma' + 'Pamela' + 'Patricia' + 'Paula' + 'Phyllis' + 'Rachel' + 'Rebecca' + 'Robin' + 'Ronda' + 'Rose' + 'Ruby' + 'Ruth' + 'Sana' + 'Sandra' + 'Sara' + 'Sarah' + 'Scarlett' + 'Shannon' + 'Sharon' + 'Shirley' + 'Stephanie' + 'Susan' + 'Tammy' + 'Teresa' + 'Tess' + 'Theresa' + 'Tina' + 'Trinity' + 'Virginia' + 'Wanda' + ] + 'Ogre Peon M': [ + # Male + 'Ba Bo' + 'Bubbage' + 'Durbo' + 'Jaro' + 'Kurger' + 'Mudwich' + 'Toe Pod' + 'Zugger' + ] + 'Ogre Peon F': [ + # Female + 'Greeke' + 'Iblet' + 'Lorba' + 'Vapa' + 'Yamra' + 'Zzoya' ] 'Potion Master': [ - 'Omar' - 'Snake' + # Male 'Amaranth' - 'Zander' + 'Alchemist' 'Arora' - 'Curie' - 'Clause' - 'Vanders' - 'Kanada' 'Artephius' + 'Clause' + 'Curie' + 'Fluvius' + 'Javin' + 'Kanada' + 'Omar' 'Paracelsus' + 'Snake' + 'Vanders' + 'Warnsdorff' + 'Zander' ] 'Librarian': [ - 'Hushbaum' - 'Matilda' - 'Agnes' + # Female 'Agathe' + 'Agnes' + 'Hushbaum' + 'Mariam' + 'Matilda' + 'Nordex' 'Satish' + 'Vera' ] 'Equestrian': [ + # Male + 'Neely' 'Reynaldo' 'Ryder' 'Thoron' - 'Mirial' - 'Neely' ] 'Knight': [ - 'Tharin' + # Male + 'Almeric' + 'Alphonse' + 'Altair' 'Arthur' - 'Galahad' - 'Mace' + 'Bertrand' + 'Bronn' + 'Bruce' 'Drake' 'Duran' - 'Almeric' - 'Hunfray' - 'Hank' - 'Jeph' - 'Neville' - 'Alphonse' 'Edward' + 'Galahad' + 'Hank' + 'Hunfray' + 'Jeph' + 'Jorah' + 'Lancelot' + 'Mace' + 'Neville' 'Shug' + 'Tharin' + 'Vint' + 'Wain' ] 'Captain': [ + # Female 'Anya' 'Brigette' - 'Sarre' - 'Katana' - 'Lily' - 'Isa' 'Dimia' - 'Jane' - 'Lia' + 'Div' 'Hardcastle' - 'Leona' - 'Jarin' 'Helena' + 'Isa' + 'Jan' + 'Jane' + 'Jarin' + 'Karp' + 'Katana' + 'Leona' + 'Lia' + 'Lily' + 'Nicks' 'Philips' + 'Sarre' + 'Sun Tzu' ] 'Ninja': [ + # Female + 'Akali' 'Amara' + 'Goemon' + 'Itachi' + 'Kennen' + 'Kosaraju' + 'Madara' + 'Minato' + 'Naruto' + 'Obito' + 'Sakura' + 'Sasuke' + 'Shen' + 'Shigeru' + 'Takashi' + 'Zed' ] 'Sorcerer': [ + # Female + 'Beazer' + 'Claude' + 'Gandalf' + 'Izzrts' + 'Kleene' 'Pender' + 'Jezebel' ] 'Samurai': [ + # Male 'Hattori' + 'Hirosha' + 'Ieyasu' + 'Izotokogawa' + 'Keitaro' + 'Miyoshi' + 'Nobunaga' + 'Yasuo' + 'Yi' ] diff --git a/app/lib/world/rand.coffee b/app/lib/world/rand.coffee index aa83a9b02..5cc0c8d41 100644 --- a/app/lib/world/rand.coffee +++ b/app/lib/world/rand.coffee @@ -7,7 +7,7 @@ class Rand @multiplier = 1664525 @modulo = 4294967296 # 2**32-1 @offset = 1013904223 - unless @seed? and 0 <= seed < @modulo + unless @seed? and 0 <= @seed < @modulo @seed = (new Date().valueOf() * new Date().getMilliseconds()) % @modulo # sets new seed value, even handling negative numbers @@ -31,4 +31,23 @@ class Rand rand2: (min, max) => min + @rand max - min + # return a random float min <= f < max + randf2: (min, max) => + min + @randf() * (max - min) + + # return a random float within range around x + randfRange: (x, range) => + x + (-0.5 + @randf()) * range + + # shuffle array in place, and also return it + shuffle: (arr) => + return arr unless arr.length > 2 + for i in [arr.length-1 .. 1] + j = Math.floor @randf() * (i - 1) + t = arr[j] + arr[j] = arr[i] + arr[i] = t + arr + + module.exports = Rand diff --git a/app/lib/world/system.coffee b/app/lib/world/system.coffee index 74b26e7d8..8c86d3c2e 100644 --- a/app/lib/world/system.coffee +++ b/app/lib/world/system.coffee @@ -46,7 +46,8 @@ module.exports = class System hashString: (s) -> return @hashes[s] if s of @hashes hash = 0 - hash = hash * 31 + s.charCodeAt(i) for i in [0 ... Math.min(s.length, 100)] + for i in [0 ... Math.min(s.length, 100)] + hash = hash * 31 + s.charCodeAt(i) hash = @hashes[s] = hash % 3.141592653589793 hash diff --git a/app/lib/world/thang.coffee b/app/lib/world/thang.coffee index 0c7721081..6797720d6 100644 --- a/app/lib/world/thang.coffee +++ b/app/lib/world/thang.coffee @@ -73,7 +73,7 @@ module.exports = class Thang for [prop, type] in props unless type in ThangState.trackedPropertyTypes # How should errors for busted Components work? We can't recover from this and run the world. - throw new Error "Type #{type} for property #{prop} is not a trackable property type: #{trackedPropertyTypes}" + throw new Error "Type #{type} for property #{prop} is not a trackable property type: #{ThangState.trackedPropertyTypes}" oldPropIndex = @trackedPropertiesKeys.indexOf prop if oldPropIndex is -1 @trackedPropertiesKeys.push prop @@ -179,6 +179,8 @@ module.exports = class Thang options.colorConfig.team = teamColor if @color and color = @grabColorConfig @color options.colorConfig.color = color + if @colors + options.colorConfig[colorType] = colorValue for colorType, colorValue of @colors options grabColorConfig: (color) -> diff --git a/app/lib/world/vector.coffee b/app/lib/world/vector.coffee index 712d99d64..0b826b6c0 100644 --- a/app/lib/world/vector.coffee +++ b/app/lib/world/vector.coffee @@ -10,7 +10,9 @@ class Vector isVector: true apiProperties: ['x', 'y', 'z', 'magnitude', 'heading', 'distance', 'dot', 'equals', 'copy', 'distanceSquared', 'rotate'] - constructor: (@x=0, @y=0, @z=0) -> + constructor: (x=0, y=0, z=0) -> + return new Vector x, y, z unless @ instanceof Vector + [@x, @y, @z] = [x, y, z] copy: -> new Vector(@x, @y, @z) @@ -109,10 +111,9 @@ class Vector invalid: () -> return (@x is Infinity) || isNaN(@x) || @y is Infinity || isNaN(@y) || @z is Infinity || isNaN(@z) - toString: (useZ) -> - useZ = true - return "{x: #{@x.toFixed(0)}, y: #{@y.toFixed(0)}, z: #{@z.toFixed(0)}}" if useZ - return "{x: #{@x.toFixed(0)}, y: #{@y.toFixed(0)}}" + toString: (precision = 2) -> + return "{x: #{@x.toFixed(precision)}, y: #{@y.toFixed(precision)}, z: #{@z.toFixed(precision)}}" + serialize: -> {CN: @constructor.className, x: @x, y: @y, z: @z} diff --git a/app/lib/world/world.coffee b/app/lib/world/world.coffee index d640d705d..0da9dd010 100644 --- a/app/lib/world/world.coffee +++ b/app/lib/world/world.coffee @@ -70,9 +70,9 @@ module.exports = class World setThang: (thang) -> thang.stateChanged = true for old, i in @thangs - console.error 'world trying to set', thang, 'over', old unless old? and thang? if old.id is thang.id @thangs[i] = thang + break @thangMap[thang.id] = thang thangDialogueSounds: (startFrame=0) -> @@ -102,7 +102,9 @@ module.exports = class World if @realTime and not @countdownFinished @realTimeSpeedFactor = 1 unless @showsCountdown - if @levelID in ['thornbush-farm', 'back-to-back', 'ogre-encampment', 'woodland-cleaver', 'shield-rush', 'peasant-protection', 'munchkin-swarm'] + if @levelID in ['woodland-cleaver', 'village-guard', 'shield-rush'] + @realTimeSpeedFactor = 2 + else if @levelID in ['thornbush-farm', 'back-to-back', 'ogre-encampment', 'peasant-protection', 'munchkin-swarm', 'munchkin-harvest', 'swift-dagger', 'shrapnel', 'arcane-ally', 'touch-of-death', 'bonemender'] @realTimeSpeedFactor = 3 if @showsCountdown return setTimeout @finishCountdown(continueLaterFn), REAL_TIME_COUNTDOWN_DELAY @@ -360,7 +362,7 @@ module.exports = class World #console.log "... world serializing frames from", startFrame, "to", endFrame, "of", @totalFrames [transferableObjects, nontransferableObjects] = [0, 0] delete flag.processed for flag in @flagHistory - o = {totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}, flagHistory: @flagHistory} + o = {totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}, flagHistory: @flagHistory, difficulty: @difficulty, scores: @getScores(), randomSeed: @randomSeed} o.trackedProperties[prop] = @[prop] for prop in @trackedProperties or [] for thangID, methods of @userCodeMap @@ -467,7 +469,7 @@ module.exports = class World w.userCodeMap[thangID][methodName][aetherStateKey] = serializedAether[aetherStateKey] else w = new World o.userCodeMap, classMap - [w.totalFrames, w.maxTotalFrames, w.frameRate, w.dt, w.scriptNotes, w.victory, w.flagHistory] = [o.totalFrames, o.maxTotalFrames, o.frameRate, o.dt, o.scriptNotes ? [], o.victory, o.flagHistory] + [w.totalFrames, w.maxTotalFrames, w.frameRate, w.dt, w.scriptNotes, w.victory, w.flagHistory, w.difficulty, w.scores, w.randomSeed] = [o.totalFrames, o.maxTotalFrames, o.frameRate, o.dt, o.scriptNotes ? [], o.victory, o.flagHistory, o.difficulty, o.scores, o.randomSeed] w[prop] = val for prop, val of o.trackedProperties perf.t1 = now() @@ -603,3 +605,10 @@ module.exports = class World teamForPlayer: (n) -> playableTeams = @playableTeams ? ['humans'] playableTeams[n % playableTeams.length] + + getScores: -> + time: @age + 'damage-taken': @getSystem('Combat')?.damageTakenForTeam 'humans' + 'damage-dealt': @getSystem('Combat')?.damageDealtForTeam 'humans' + 'gold-collected': @getSystem('Inventory')?.teamGold.humans?.collected + 'difficulty': @difficulty diff --git a/app/lib/world/world_script_note.coffee b/app/lib/world/world_script_note.coffee index be342cee7..651e72296 100644 --- a/app/lib/world/world_script_note.coffee +++ b/app/lib/world/world_script_note.coffee @@ -6,7 +6,7 @@ module.exports = class WorldScriptNote constructor: (script, @event, world) -> return unless script? @invalid = true - return unless scriptMatchesEventPrereqs(script, event) + return unless scriptMatchesEventPrereqs(script, @event) # Could add the scriptPrereqsSatisfied or seen/repeats stuff if needed @invalid = false @channel = script.channel diff --git a/app/locale/ar.coffee b/app/locale/ar.coffee index fcd3d0421..b6a479fd2 100644 --- a/app/locale/ar.coffee +++ b/app/locale/ar.coffee @@ -3,14 +3,15 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi slogan: "تعلّم البرمجة من لعب لعبة" no_ie: "CodeCombat لا يعمل في Internet Explorer 8 أو أقل. آسف!" # Warning that only shows up in IE8 and older no_mobile: "لم يصمم CodeCombat للهواتف النقالة وقد لا يعمل!" # Warning that shows up on mobile devices - play: "إلعب" # The big play button that just starts playing a level + play: "إلعب" # The big play button that opens up the campaign view. old_browser: "اه أوه، متصفحك قديم جدا لتشغيل CodeCombat. آسف!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "يمكنك محاولة على أي حال، لكنه ربما لن يعمل." + ipad_browser: "الخبر السيئ: لا يمكنك تشغيل اللعبة في المتصفح على الآيباد ، الخبر الجيد: تطبيق اللعبة للآيباد جاهز و في إنتظار موافقة آبل." campaign: "حملة" for_beginners: "للمبتدئين" multiplayer: "متعدد اللاعبين" # Not currently shown on home page for_developers: "للمطوّرين" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "أو حمِّل من أجل الآيباد" nav: play: "إلعب" # The top nav bar entry where players choose which levels to play @@ -51,82 +52,82 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi spectate: "مشاهد" # Ladder page players: "لاعبين" # Hover over a level on /play hours_played: "ساعات اللّعب" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + items: "العناصر" # Tooltip on item shop button from /play + unlock: "فتح" # For purchasing items and heroes + confirm: "تأكيد" + owned: "ممتَلَك" # For items you own + locked: "مقفل" + purchasable: "للشراء" # For a hero you unlocked but haven't purchased + available: "متوفر" + skills_granted: "مهارات الممنوحة" # Property documentation details + heroes: "الأبطال" # Tooltip on hero shop button from /play + achievements: "الإنجازات" # Tooltip on achievement list button from /play + account: "حساب" # Tooltip on account button from /play + settings: "الإعدادات" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "التالي" # Go from choose hero to choose inventory before playing a level + change_hero: "تغيير البطل" # Go back from choose inventory to choose hero + choose_inventory: "فريق الأصناف" + buy_gems: "شراء الأحجار الكريمة" + subscription_required: "الإشتراك إلزامي" + anonymous: "لاعب مجهول" level_difficulty: "الصعوبة:" campaign_beginner: "حملة المبتدئين" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "اختر مستواك" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "يمكنك القفز إلى أي مستوى أدناه، أو مناقشة المستويات على " - adventurer_forum: "منتدى المغامر" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... فيها تتعلم سحر البرمجة." - campaign_dev: "مستويات أصعب عشوائية" - campaign_dev_description: "... فيها تتعلم واجهة بينما تفعل شيئا أصعب قليلا." + awaiting_levels_adventurer_prefix: "نحن الافراج عن مستويات جديدة كل أسبوع." + awaiting_levels_adventurer: "التوقيع على النحو المغامر" + awaiting_levels_adventurer_suffix: "أن تكون أول للعب مستويات جديدة." + adjust_volume: "تعديل الصوت" campaign_multiplayer: "ساحات متعددة اللاّعبين" campaign_multiplayer_description: "... فيها تبرمج وجه لوجه ضد لاعبين آخرين." - campaign_player_created: "أنشئ اللاّعب" - campaign_player_created_description: "... فيها تقاتل ضد الإبداع الخاص بـزميلك<a href=\"/contribute#artisan\"> الحرفيّ الساحر</a>." - campaign_classic_algorithms: "الخوارزميات التقليديّة" - campaign_classic_algorithms_description: "... فيها تتعلّم خوارزميّات الأكثر شعبيّة في علوم الحاسب الآلي." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" + campaign_old_multiplayer_description: "قطع اثرية من العصر أكثر تحضرا. يتم تشغيل أي محاكاة لهذه السن، الساحات متعددة البطل أقل." + + share_progress_modal: + blurb: "كنت تقدما كبيرا! أخبر والديك وكم كنت قد تعلمت مع CodeCombat." + email_invalid: "البريد الاكتروني غير صالح." + form_blurb: "ادخل بريد اولياء امرك الالكتروني لكي نريهم!" + form_label: "عنوان البريد الالكتروني" + placeholder: "عنوان البريد الالكتروني" + title: "عمل ممتاز, Apprentice" login: sign_up: "إنشاء حساب" log_in: "تسجيل الدخول" logging_in: "جاري تسجيل الدخول" log_out: "تسجيل الخروج" - recover: "إستعادة حساب" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "نسيت رمز الدخول?" + authenticate_gplus: "مصادقة G+" + load_profile: "تحميل صفحة غوغل بلس" + finishing: "الانتهاء" + sign_in_with_facebook: "سجل الدخول بواسطة فيسبوك" + sign_in_with_gplus: "سجل الدخول بواسطة غوغل بلس" + signup_switch: "تريد انشاء حساب?" signup: - create_account_title: "إنشاء حساب لحفظ التقدّم" - description: "إنه مجاني. فقط بحاجة بضعة أشياء وسوف تكون على ما يرام للبدء:" email_announcements: "تلقي الإعلانات عن طريق البريد الإلكتروني" - coppa: "13+ أو لست من الولايات المتّحدة الأمريكيّة" - coppa_why: "(لماذا؟)" creating: "جاري إنساء الحساب..." sign_up: "التسجيل" log_in: "تسجيل الدّخول بكلمة السرّ" social_signup: "أو، يمكنك الاشتراك من خلال الفايسبوك أو جوجل+" required: "تحتاج إلى تسجيل الدخول قبل أن تتمكن من السير في هذا الطريق." + login_switch: "لديك حساب بالفعل?" recover: recover_account_title: "إستعادة حساب" send_password: "إرسال كلمة سرّ الإستعادة" -# recovery_sent: "Recovery email sent." + recovery_sent: "استرداد البريد الالكتروني المرسل." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "الأولي" + secondary: "ثانوي" + armor: "درع" + accessories: "إكسسوارات" + misc: "متفرقات" + books: "كتب" common: + back: "الرجوع" # When used as an action verb, like "Navigate backward" + continue: "امض قدما" # When used as an action verb, like "Continue forward" loading: "تحميل" saving: "جاري الحفض" sending: "جاري الإرسال" @@ -139,41 +140,62 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi fork: "إنسخ" play: "إلعب" # When used as an action verb, like "Play next level" retry: "إعادة" + actions: "Actions" + info: "معلومات" + help: "مساعدة" watch: "مشاهدة" unwatch: "إنهاء المشاهدة" submit_patch: "تقديم التصحيح" + submit_changes: "تقديم التغييرات" + save_changes: "حفظ التغيرات" -# general: -# and: "and" -# name: "Name" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" -# or: "or" -# subject: "Subject" -# email: "Email" -# password: "Password" -# message: "Message" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + general: + and: "و" + name: "اسم" + date: "تأريخ" + body: "جسد" + version: "نسخة" + pending: "ارسال" + accepted: "مقبول" + rejected: "مرفوض" + withdrawn: "متعادل" + submitter: "المقدم" + submitted: "تم التقديم" + commit_msg: "حول رسالة" + review: "مشاهدة" + version_history: "تاريخ النسخة" + version_history_for: "تاريخ النسخة لل: " + select_changes: "اختر تغيريين." + undo_prefix: "فك" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Redo" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "شاهد مقطع فيديو عن المستوى الحالي" + result: "نتيجة" + results: "نتائج" + description: "وصف" + or: "او" + subject: "موضوع" + email: "البريد الالكتروني" + password: "الرمز السري" + message: "رسالة" + code: "رمز" + ladder: "سلم" + when: "متى" + opponent: "المقابل" + rank: "مرتبة" + score: "نقاط" + win: "فوز" + loss: "خسارة" + tie: "ربطة عنق" + easy: "سهل" + medium: "متوسط" + hard: "صعب" + player: "لاعب" + player_level: "مستوى" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "محارب" + ranger: "مقاتل" + wizard: "ساحر" units: second: "ثانيّة" @@ -191,61 +213,62 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi year: "سنة" years: "سنوات" -# play_level: -# done: "Done" -# home: "Home" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" -# guide: "Guide" -# restart: "Restart" -# goals: "Goals" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" -# action_timeline: "Action Timeline" -# click_to_select: "Click on a unit to select it." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" -# reload_title: "Reload All Code?" -# reload_really: "Are you sure you want to reload this level back to the beginning?" -# reload_confirm: "Reload All" -# victory_title_prefix: "" -# victory_title_suffix: " Complete" -# victory_sign_up: "Sign Up to Save Progress" -# victory_sign_up_poke: "Want to save your code? Create a free account!" -# victory_rate_the_level: "Rate the level: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" -# victory_go_home: "Go Home" # Only in old-style levels. -# victory_review: "Tell us more!" # Only in old-style levels. + play_level: + done: "انتهاء" + home: "Home" # Not used any more, will be removed soon. + level: "مستوى" # Like "Level: Dungeons of Kithgard" + skip: "الغاء" + game_menu: "قائمة اللعبة" + guide: "المرشد" + restart: "اعادة التشغيل" + goals: "الاهداف" + goal: "هدف" + running: "جاري التشغيل..." + success: "نجاح!" + incomplete: "غير مكتمل" + timed_out: "تم انتهاء الوقت" + failing: "فشل" + action_timeline: "عمل الجدول الزمني" + click_to_select: "انقر على وحدة لتحديده." + control_bar_multiplayer: "متعددة" + control_bar_join_game: "تاريخ لعبة" + reload: "تحديث" + reload_title: "تحديث الصفحة كل رمز؟" + reload_really: "هل أنت متأكد أنك تريد تحميل هذا المستوى مرة أخرى إلى البداية؟" + reload_confirm: "تحديث جميع" + victory: "فوز" + victory_title_prefix: "" + victory_title_suffix: " كامل" + victory_sign_up: "اشترك لإنقاذ التقدم" + victory_sign_up_poke: "تريد حفظ التعليمات البرمجية الخاصة بك؟ إنشاء حساب مجاني!" + victory_rate_the_level: "معدل المستوى: " # Only in old-style levels. + victory_return_to_ladder: "العودة إلى سلم" + victory_play_continue: "استمر" + victory_saving_progress: "توفير التقدم" + victory_go_home: "اذهب للمنزل" # Only in old-style levels. + victory_review: "إخبرنا المزيد!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" -# guide_title: "Guide" -# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. -# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. -# tome_other_units: "Other Units" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" + victory_hour_of_code_done_yes: "نعم، أنا مع الانتهاء من ساعتي من قانون ™!" +# victory_experience_gained: "XP Gained" + victory_gems_gained: "الأحجار الكريمة المكتسبة" + victory_new_item: "عنصر جديد" + victory_viking_code_school: "يدخن المقدسة، وكان ذلك على مستوى الثابت الذي فاز فقط! إذا لم تكن بالفعل مطور برامج، يجب أن تكون. كنت فقط حصلت على المسار السريع لقبوله مع مدرسة فايكنغ المدونة، حيث يمكنك أن تأخذ المهارات الخاصة بك إلى المستوى التالي وتصبح مطور ويب محترف في 14 أسبوعا." + victory_become_a_viking: "تصبح فايكنغ" + guide_title: "دليل" + tome_minion_spells: "نوبات التوابع الخاصة بك" # Only in old-style levels. + tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. + tome_other_units: "وحدات أخرى" # Only in old-style levels. + tome_cast_button_run: "Run" + tome_cast_button_running: "تشغيل" + tome_cast_button_ran: "ران" # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -318,20 +383,94 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # equip: "Equip" # unequip: "Unequip" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." + buy_gems: + few_gems: " عدد قليل من الأحجار الكريمة" + pile_gems: "كومة من الأحجار الكريمة" + chest_gems: "الصدر من الأحجار الكريمة" + purchasing: "شراء ..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" + prompt_button: "أدخل متجر" + recovered: "الأحجار الكريمة السابقة أون استردادها. يرجى تحديث الصفحة." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" + choose_hero: "اختر بطلك" + programming_language: "لغة البرمجة" # programming_language_description: "Which programming language do you want to use?" # default: "Default" -# experimental: "Experimental" + experimental: "تجريبي" python_blurb: "بسيطة لكنها قوية، بيثون هي لغة برمجة عظيمة للأغراض العامة." javascript_blurb: "لغة الويب. عظيم للكتابة المواقع، تطبيقات الويب، ألعاب HTML5، والخوادم." coffeescript_blurb: "Nicer JavaScript syntax." @@ -339,17 +478,30 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi lua_blurb: "لعبة لغة البرمجة." io_blurb: "بسيطة ولكنها غامضة." # status: "Status" -# weapons: "Weapons" +# hero_type: "Type" + weapons: "اسلحة" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" # weapons_wizard: "Wands, Staffs - Long Range, Magic" # attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" + health: "الصحة" + speed: "سرعة" + regeneration: "تجديد" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" + skills: "المهارات" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,51 +562,152 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "إحفض نسخة جديدة" new_major_version: "نسخة مهمّة جديدة" +# submitting_patch: "Submitting Patch..." cla_prefix: "لحفظ التغييرات، أولا يجب أن توافق على " cla_url: "اتفاقيّة ترخيص المساهم" cla_suffix: "." cla_agree: "أوافق" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "الاتّصال بـ CodeCombat" welcome: "جيد أن نسمع منك! استخدام هذا النموذج لترسل لنا البريد الإلكتروني." - contribute_prefix: "إذا كنت ترغب في المساهمة، تحقّق من " - contribute_page: "صفحة المساهة" - contribute_suffix: "!" forum_prefix: "لأي شيء عام، يرجى المحاولة" forum_page: "منتدانا" forum_suffix: "بدلا من ذلك." +# faq_prefix: "There's also a" + faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "إرسال تعليقات" contact_candidate: "الاتصال المرشح" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated -# account_settings: -# title: "Account Settings" -# not_logged_in: "Log in or create an account to change your settings." -# autosave: "Changes Save Automatically" -# me_tab: "Me" -# picture_tab: "Picture" + account_settings: + title: "إعدادات الحساب" + not_logged_in: "تسجيل الدخول أو إنشاء حساب لتغيير الإعدادات الخاصة بك." + autosave: "تغييرات حفظ تلقائيا" + me_tab: "أنا" + picture_tab: "صورة" + delete_account_tab: "حذف حسابك الخاص" + wrong_email: "Email خاطئ" + wrong_password: "كلمة مرور خاطئة" # upload_picture: "Upload a picture" + delete_this_account: "حذف هذا الحساب بشكل دائم" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" -# new_password: "New Password" -# new_password_verify: "Verify" -# email_subscriptions: "Email Subscriptions" + new_password: "كلمة سر جديدة" + new_password_verify: "تحقق من" + type_in_email: "اكتب في البريد الإلكتروني الخاص بك لتأكيد الحساب الحذف." + type_in_password: "أيضا، اكتب كلمة المرور الخاصة بك." + email_subscriptions: "الاشتراكات البريد الإلكتروني" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" # email_announcements_description: "Get emails on the latest news and developments at CodeCombat." @@ -481,13 +732,12 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi no_achievements: "لا توجد انجازات مكتسبة حتّى الآن." favorite_prefix: "لغتك المفضّلة هي " favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." achievements: last_earned: "المكتسبات الأخيرة" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi account: recently_played: "لعبت مؤخّرا" no_recent_games: "لا يوجد لعب لعبت خلال الأسبوعين الماضيين." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "خطأ في تحميل من الخادم" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" delta: added: "أضيفت" modified: "معدّلة" +# not_modified: "Not Modified" deleted: "حذفت" moved_index: "فهرس انتقل" text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "إعدادات الساحر" - customize_avatar: "الصورة الرمزية الخاصة بك" - active: "نشيط" - color: "لون" - group: "فريق" - clothes: "ملابس" - trim: "الحالة" - cloud: "سحابة" - team: "فريق" - spell: "سحر" - boots: "أحذية" - hue: "Hue" - saturation: "صفاء اللون" - lightness: "إضاءة" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/bg.coffee b/app/locale/bg.coffee index a6f44ba04..e1fe0af41 100644 --- a/app/locale/bg.coffee +++ b/app/locale/bg.coffee @@ -1,28 +1,29 @@ module.exports = nativeDescription: "български език", englishDescription: "Bulgarian", translation: home: - slogan: "Научи се да програмираш, докато играеш игра " + slogan: "Научи се да програмираш, докато играеш" no_ie: "CodeCombat не работи под Internet Explorer 8 или по-стари версии. Съжалявам!" # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat не е направен за мобилни устройства и може да не работи!" # Warning that shows up on mobile devices - play: "Играй" # The big play button that just starts playing a level + no_mobile: "CodeCombat не е направен за мобилни устройства и може да не работи с тях!" # Warning that shows up on mobile devices + play: "Играй" # The big play button that opens up the campaign view. old_browser: "О, не! Браузърът ти е твърде стар за CodeCombat. Съжалявам!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Все пак можеш да опиваш, но най-вероятно няма да проработи." -# campaign: "Campaign" + ipad_browser: "Лошa новинa: CodeCombat не работи в браузъра на iPad. Добра новина: Приложението ни за iPad изчаква одобрение от Apple." + campaign: "Кампания" for_beginners: "За начинаещи" # multiplayer: "Multiplayer" # Not currently shown on home page for_developers: "За разработчици" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Или свали за iPad" nav: play: "Нива" # The top nav bar entry where players choose which levels to play - community: "Обшност" + community: "Общност" editor: "Редактор" blog: "Блог" forum: "Форум" - account: "Сметката" + account: "Акаунт" profile: "Профил" stats: "Статистики" -# code: "Code" -# admin: "Admin" # Only shows up when you are an admin + code: "Код" + admin: "Администратор" # Only shows up when you are an admin home: "Начало" contribute: "Допринеси" # legal: "Legal" @@ -47,71 +48,69 @@ module.exports = nativeDescription: "български език", englishDescri subscribe_as_diplomat: "Стани дипломат" play: -# play_as: "Play As" # Ladder page -# spectate: "Spectate" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play + play_as: "Играй като" # Ladder page + spectate: "Наблюдател" # Ladder page + players: "Играчи" # Hover over a level on /play + hours_played: "Изиграни часове" # Hover over a level on /play items: "Предмети" # Tooltip on item shop button from /play unlock: "Отключи" # For purchasing items and heroes confirm: "Потвърди" -# owned: "Owned" # For items you own + owned: "Придобити" # For items you own locked: "Заключено" -# available: "Available" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased + available: "Достъпен" # skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero + heroes: "Герои" # Tooltip on hero shop button from /play + achievements: "Постижения" # Tooltip on achievement list button from /play + account: "Акаунт" # Tooltip on account button from /play + settings: "Настройки" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "Напред" # Go from choose hero to choose inventory before playing a level + change_hero: "Смени герой" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" buy_gems: "Купи скъпоценни камъни" - older_campaigns: "Предишни капмании" + subscription_required: "Нужен е абонамент" anonymous: "Анонимен играч" level_difficulty: "Трудност" campaign_beginner: "Кампания за начинаещи" -# awaiting_levels_adventurer_prefix: "We release five levels per week." + awaiting_levels_adventurer_prefix: "5 нови нива всяка седмица" # {change} awaiting_levels_adventurer: "Стани Приключенец" awaiting_levels_adventurer_suffix: "за да бъдеш първият, който играе нови нива." - choose_your_level: "Избери своето ниво" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " - adventurer_forum: "Приключенският форум" -# adventurer_suffix: "." - campaign_old_beginner: "Предишни кампании за начинаещи" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." - campaign_dev: "Случайни трудни нива" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." + adjust_volume: "Настрой звук" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." - campaign_classic_algorithms: "Класически алгоритми" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Справяш се чудесно! Сподели с някого,колко много научи чрез CodeCombat." + email_invalid: "Имейл адресът е невалиден" +# form_blurb: "Enter your parent's email below and we’ll show them!" + form_label: "Електронна поща" + placeholder: "Имейл адрес" +# title: "Excellent Work, Apprentice" login: sign_up: "Създай Профил" log_in: "Вход" logging_in: "Влизане..." log_out: "Изход" - recover: "Възстанови акаунт" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" + forgot_password: "Забравена парола?" + authenticate_gplus: "Автентикация чрез G+" + load_profile: "Зареди G+ профил" # finishing: "Finishing" + sign_in_with_facebook: "Вписване чрез Facebook" + sign_in_with_gplus: "Вписване чрез G+" + signup_switch: "Създаване на нов акаунт?" signup: - create_account_title: "Създавай нов акаунт, за да запазиш прогреса си" - description: "Е безплатен. Само ще трабва неколко неща и ти ще си готов:" email_announcements: "Получава анонси по имейл" -# coppa: "13+ or non-USA " - coppa_why: "(Защо?)" creating: "Създаване на профил..." sign_up: "Регистриране" log_in: "Вход с парола" social_signup: "Или, можеш да се регистрираш през Facebook или G+:" required: "Трабва да влезеш преди можеш да ходиш на там." + login_switch: "Вече имаш акаунт?" recover: recover_account_title: "Възстанови Акаунт" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "български език", englishDescri # books: "Books" common: + back: "Назад" # When used as an action verb, like "Navigate backward" + continue: "Продължи" # When used as an action verb, like "Continue forward" loading: "Зареждане..." saving: "Записване..." sending: "Изпращане..." @@ -135,13 +136,18 @@ module.exports = nativeDescription: "български език", englishDescri save: "Запис" publish: "Публикувай" create: "Създай" -# manual: "Manual" + manual: "Ръчно" # fork: "Fork" -# play: "Play" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + play: "Играй" # When used as an action verb, like "Play next level" + retry: "Отново" + actions: "Действия" + info: "Инфо" + help: "Помощ" + watch: "Наблюдавай" + unwatch: "Не наблюдавай" + submit_patch: "Предложи кръпка" + submit_changes: "Предложи промените" +# save_changes: "Save Changes" general: and: "и" @@ -149,31 +155,47 @@ module.exports = nativeDescription: "български език", englishDescri date: "Дата" # body: "Body" version: "Версия" + pending: "Изчакващ" + accepted: "Прието" + rejected: "Отказано" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" + review: "Преглед" version_history: "Предишни версии" -# version_history_for: "Version History for: " -# result: "Result" + version_history_for: "Предишни версии на: " + select_changes: "Избери две промени за да видиш разликата между тях." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" + result: "Резултат" results: "Резултати" description: "Описание" or: "или" -# subject: "Subject" + subject: "Тема" email: "Email" password: "Парола" message: "Съобщение" -# code: "Code" + code: "Код" # ladder: "Ladder" # when: "When" -# opponent: "Opponent" + opponent: "Опонент" # rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" + score: "Точки" + win: "Победа" + loss: "Загуба" # tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + easy: "Лесно" + medium: "Средно" + hard: "Трудно" + player: "Играч" + player_level: "Ниво" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Боец" +# ranger: "Ranger" + wizard: "Магьосник" units: second: "секунда" @@ -194,45 +216,45 @@ module.exports = nativeDescription: "български език", englishDescri play_level: done: "Готово" # home: "Home" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" -# guide: "Guide" -# restart: "Restart" -# goals: "Goals" -# goal: "Goal" + level: "Ниво" # Like "Level: Dungeons of Kithgard" + skip: "Прескочи" + game_menu: "Главно Меню" + guide: "Упътване" + restart: "Рестарт" + goals: "Цели" + goal: "Цел" # running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" + success: "Успех!" + incomplete: "Недовършен" # timed_out: "Ran out of time" # failing: "Failing" # action_timeline: "Action Timeline" # click_to_select: "Click on a unit to select it." # control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" -# reload_title: "Reload All Code?" + control_bar_join_game: "Присъединяване" + reload: "Презареди" + reload_title: "Презареди целият код?" # reload_really: "Are you sure you want to reload this level back to the beginning?" -# reload_confirm: "Reload All" + reload_confirm: "Презареди всички" + victory: "Победа" # victory_title_prefix: "" # victory_title_suffix: " Complete" -# victory_sign_up: "Sign Up to Save Progress" + victory_sign_up: "Регистрирай се за да запишеш напредъка си" # victory_sign_up_poke: "Want to save your code? Create a free account!" # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "Продължи" + victory_saving_progress: "Записване на напредъка" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. -# victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" -# guide_title: "Guide" + victory_hour_of_code_done: "Готов ли си?" + victory_hour_of_code_done_yes: "Да аз съм готов с моят Hour of Code™!" + victory_experience_gained: "Спечелен опит" + victory_gems_gained: "Спечелени скъпоценни камъни" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" + guide_title: "Упътване" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. # tome_other_units: "Other Units" # Only in old-style levels. @@ -242,30 +264,38 @@ module.exports = nativeDescription: "български език", englishDescri # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" -# tome_your_skills: "Your Skills" + tome_your_skills: "Твоите Умения" + tome_help: "Помощ" # tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" + hud_continue_short: "Продължи" + code_saved: "Кодът е записан" + skip_tutorial: "Пропусни (esc)" + keyboard_shortcuts: "Клавишни комбинации" + loading_ready: "Готово!" + loading_start: "Стартирай Ниво" + problem_alert_title: "Оправи си кода." + problem_alert_help: "Помощ" + time_current: "Текущо време:" # time_total: "Max:" # time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "Пробвай отново" + infinite_loop_reset_level: "Ресетване на Ниво" + infinite_loop_comment_out: "Коментирай моят Код" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." + tip_open_source: "CodeCombat e 100% проект с отворен код!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat стартира своята beta през Октомври, 2013." + tip_think_solution: "Помисли върху решението,не проблема." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" # tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" # tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" @@ -289,26 +319,61 @@ module.exports = nativeDescription: "български език", englishDescri # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." -# game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + game_menu: + inventory_tab: "Инвентар" + save_load_tab: "Запиши/Зареди" + options_tab: "Настройки" + guide_tab: "Упътване" + guide_video_tutorial: "Видео Упътване" + guide_tips: "Съвети" # multiplayer_tab: "Multiplayer" -# auth_tab: "Sign Up" + auth_tab: "Записване" # inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" + choose_hero_caption: "Избери герой, език" # save_load_caption: "... and view history" -# options_caption: "Configure settings" + options_caption: "Промени настройките" # guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + multiplayer_caption: "Играй с приятели!" + auth_caption: "Запиши напредъка си." + + leaderboard: +# leaderboard: "Leaderboard" + view_other_solutions: "Виж други решения" # {change} + scores: "Точки" + top_players: "ТОП играчи според" + day: "Днес" + week: "Тази седмица" + all: "От самото начало" + time: "Време" + damage_taken: "Поети щети" + damage_dealt: "Нанесени щети" + difficulty: "Трудност" + gold_collected: "Събрано Злато" # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -318,13 +383,87 @@ module.exports = nativeDescription: "български език", englishDescri # equip: "Equip" # unequip: "Unequip" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + buy_gems: + few_gems: "Няколко скъпоценни камъни" + pile_gems: "Купчина скъпоценни камъни" + chest_gems: "Сандък със скъпоценни камъни" + purchasing: "Купуване..." + declined: "Картата ви беше отказана." + retrying: "Грешка в сървъра, пробвам отново." + prompt_title: "Недостатъчно скъпоценни камъни" + prompt_body: "Искате ли още?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" + feature5: "Видео уроци" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" + subscribe_title: "Абонирай се" + unsubscribe: "Прекрати абонамента" + confirm_unsubscribe: "Подтвърди прекратяване на абонамента" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." + unsubscribe_feedback_placeholder: "O, къде сбъркахме?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" + parents: "За Родители" + parents_title: "Скъпи Родители: Вашето дете се учи да програмира. Ще му помогнете ли да продължи?" + parents_blurb1: "Вашето дете изигра __nLevels__ нива и научи основи на програмирането. Развийте интереса у тях като им купите абонамент - така те ще могат да продължат с игрите." + parents_blurb1a: "Програмирането е важно умение което без съмнение вашето дете ще използва когато порасне. До 2020-та, основни познания по програмиране ще са необходими за повече от 77% от работните места, а софтуерните инженери ще са много търсени по света. Знаете ли че диплома в сферата на информационните технологии е предпоставка за едни от най-високите заплати в индустрията?" + parents_blurb2: "За $9.99 USD/месец, вашето дете ще получава нови задачи всяка седмица, както и персонална помощ по електронната помощ от професионални програмисти." + parents_blurb3: "Без Риск: 100% гаранция за възстановяване на средствата, прекратяване на абонамаента с едно натискане на бутон." + payment_methods: "Начини на плащане" + payment_methods_title: "Възможни начини на плащане" + payment_methods_blurb1: "В момента приемаме кредитни карти и Alipay." + payment_methods_blurb2: "Ако желаете алтернативна форма на плащане, свържете се с нас" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "български език", englishDescri # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "български език", englishDescri # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "български език", englishDescri # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "български език", englishDescri # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "български език", englishDescri # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "български език", englishDescri # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "български език", englishDescri # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "български език", englishDescri # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "български език", englishDescri # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "български език", englishDescri # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "български език", englishDescri # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Преглед" edit_article_title: "Промени статията" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "български език", englishDescri # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "български език", englishDescri # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "български език", englishDescri # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "български език", englishDescri # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "български език", englishDescri # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "български език", englishDescri # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "български език", englishDescri # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "български език", englishDescri # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "български език", englishDescri # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "български език", englishDescri # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "български език", englishDescri # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "български език", englishDescri # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "български език", englishDescri # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/ca.coffee b/app/locale/ca.coffee index b4062a62b..fcd1a6d5d 100644 --- a/app/locale/ca.coffee +++ b/app/locale/ca.coffee @@ -1,16 +1,17 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", translation: home: - slogan: "Aprén a programar tot Jugant" + slogan: "Aprèn a programar tot Jugant" no_ie: "CodeCombat no funciona en Internet Explorer 8 o versions anteriors. Perdó!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat no ha estat dissenyat per dispositius mòbils i per tant no funcionarà!" # Warning that shows up on mobile devices - play: "Jugar" # The big play button that just starts playing a level + play: "Jugar" # The big play button that opens up the campaign view. old_browser: "Uh oh, el teu navegador és massa antic per fer funcionar CodeCombat. Perdó!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Pots probar-ho igualment, però el més segur és que no funcioni." + ipad_browser: "Males notícies: CodeCombat no funciona en el navegador d'iPad. Bones notícies: la nostre aplicació d'iPad està esperant l'aprovació d'Apple." campaign: "Campanya" for_beginners: "Per a principiants" multiplayer: "Multijugador" # Not currently shown on home page for_developers: "Per a Desenvolupadors" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "O descarrèga'l per iPad" nav: play: "Nivells" # The top nav bar entry where players choose which levels to play @@ -41,10 +42,10 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr diplomat_suggestion: title: "Ajuda a traduir CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "Neccesitem les teves habilitats lingüístiques." - pitch_body: "Hem desembolupat CodeCombat en Anglès, peró tenim jugadors per tot el món. Molts d'ells volen jugar en Català, però no parlen anglès, per tant si pots parlar ambdós llengües, siusplau considereu iniciar sesió per a ser Diplomàtic i ajudar a traduir la web de CodeCombat i tots els seus nivell en Català." - missing_translations: "Fins que puguem traduir-ho tot en Català, veuràs en anglès quant sigui possible." - learn_more: "Apren més sobre seru un diplomàtic" - subscribe_as_diplomat: "Subscriute com a diplomàtic" + pitch_body: "Hem desenvolupat CodeCombat en anglès, peró tenim jugadors per tot el món. Molts d'ells volen jugar en Català, però no parlen anglès, per tant si pots parlar ambdós llengües, siusplau considereu iniciar sesió per a ser Diplomàtic i ajudar a traduir la web de CodeCombat i tots els seus nivell en Català." + missing_translations: "Fins que puguem traduir-ho tot en català, ho veuràs en anglès quant no estigui en català." + learn_more: "Aprèn més sobre ser un diplomàtic" + subscribe_as_diplomat: "Subscriu-te com a diplomàtic" play: play_as: "Jugar com" # Ladder page @@ -52,66 +53,64 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr players: "jugadors" # Hover over a level on /play hours_played: "hores de joc" # Hover over a level on /play items: "Objectes" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details + unlock: "Desbloquejar" # For purchasing items and heroes + confirm: "Confirmar" + owned: "Adquirit" # For items you own + locked: "Bloquejat" + purchasable: "Comprable" # For a hero you unlocked but haven't purchased + available: "Disponible" + skills_granted: "Habilitats Garantides" # Property documentation details heroes: "Herois" # Tooltip on hero shop button from /play achievements: "Triomfs" # Tooltip on achievement list button from /play - account: "Conta" # Tooltip on account button from /play + account: "Compte" # Tooltip on account button from /play settings: "Configuració" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play next: "Següent" # Go from choose hero to choose inventory before playing a level change_hero: "Canviar heroi" # Go back from choose inventory to choose hero choose_inventory: "Equipar objectes" -# buy_gems: "Buy Gems" - older_campaigns: "Campanyes antigues" + buy_gems: "Comprar Gemes" + subscription_required: "Subscripció necessària" anonymous: "Jugador anònim" level_difficulty: "Dificultat: " campaign_beginner: "Campanya del principiant" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Escull el teu nivell" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Pots saltar a qualsevols dels nivells de més abaix, o discutir els nivells de més amunt." - adventurer_forum: "El fòrum de l'aventurer" - adventurer_suffix: "." - campaign_old_beginner: "Antiga campanya del principiant" - campaign_old_beginner_description: "... on aprens la bruixeria de la programació." - campaign_dev: "Nivells difícils aleatoris" - campaign_dev_description: "... on aprens a interactuar amb la interfície tot fent coses un pèl més difícils." + awaiting_levels_adventurer_prefix: "Fem cinc nivells per setmana" # {change} + awaiting_levels_adventurer: "Inicia sessió com aventurer" + awaiting_levels_adventurer_suffix: "sigues el primer en jugar els nous nivells" + adjust_volume: "Ajustar volum" campaign_multiplayer: "Arenes Multijugador" campaign_multiplayer_description: "... on programes cara a cara contra altres jugadors." - campaign_player_created: "Creats pel Jugador" - campaign_player_created_description: "... on lluites contra la creativitat dels teus companys <a href=\"/contribute#artisan\">Artisan Wizards</a>." - campaign_classic_algorithms: "Algoritmes classics" - campaign_classic_algorithms_description: "... on pots aprendre els algoritmes més populars de l'informàtica." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Estàs progressant molt! Digues a algú quan n'has après amb CodeCombat." # {change} + email_invalid: "Correu electrònic invalid." + form_blurb: "Escriu els seus emails a sota i els hi ensenyarem!" + form_label: "Correu electrònic" + placeholder: "adreça de correu electrònic" + title: "Excelent feina, Aprenent" login: sign_up: "Crear un compte" log_in: "Iniciar Sessió" logging_in: "Iniciant Sessió" log_out: "Tancar Sessió" - recover: "Recuperar un compte" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Contrasenya oblidada?" + authenticate_gplus: "Inicia amb G+" + load_profile: "Carrega un perfil de G+" + finishing: "Acabant" + sign_in_with_facebook: "Inicia amb Facebook" + sign_in_with_gplus: "Inicia amb G+" + signup_switch: "Vols crear-te un compte?" signup: - create_account_title: "Crear un compte per tal de guardar els progressos" - description: "És gratuit. Només calen un parell de coses i ja podràs començar:" email_announcements: "Rebre anuncis via email" - coppa: " més de 13 anys o fora dels Estats Units" - coppa_why: "(Per què?)" creating: "Creant Compte..." sign_up: "Registrar-se" log_in: "Iniciar sessió amb la teva contrasenya" - social_signup: "O, pots iniciar sesió desde Facebook o G+:" - required: "Neccesites iniciar sesió abans ." + social_signup: "O, pots iniciar sessió desde Facebook o G+:" + required: "Neccesites iniciar sessió abans ." + login_switch: "Ja tens un compte?" recover: recover_account_title: "Recuperar Compte" @@ -119,14 +118,16 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr recovery_sent: "Correu de recuperació de contrasenya enviat." items: -# primary: "Primary" -# secondary: "Secondary" + primary: "Primari" + secondary: "Secundari" armor: "Armadura" accessories: "Accessoris" -# misc: "Misc" -# books: "Books" + misc: "Misc" + books: "Llibres" common: + back: "Endarrere" # When used as an action verb, like "Navigate backward" + continue: "Continua" # When used as an action verb, like "Continue forward" loading: "Carregant..." saving: "Guardant..." sending: "Enviant..." @@ -139,41 +140,62 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr fork: "Fork" play: "Jugar" # When used as an action verb, like "Play next level" retry: "Tornar a intentar" + actions: "Accions" + info: "Info" + help: "Ajuda" watch: "Veure" unwatch: "Amaga" submit_patch: "Enviar pegat" + submit_changes: "Puja canvis" +# save_changes: "Save Changes" general: -# and: "and" + and: "i" name: "Nom" date: "Data" body: "Cos" version: "Versió" -# commit_msg: "Commit Message" + pending: "Pendent" + accepted: "Acceptat" + rejected: "Rebutjat" + withdrawn: "Retirat" + submitter: "Remitent" + submitted: "Presentat" + commit_msg: "nissatge de Commir" + review: "Revisió" version_history: "Historial de versions" -# version_history_for: "Version History for: " + version_history_for: "Historial de versions per: " + select_changes: "Seleciona dos canvis sota per veure les diferencies." + undo_prefix: "Desfer" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Refés" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Reproduir avanç del nivell actual" result: "Resultat" results: "Resultats" description: "Descripció" -# or: "or" -# subject: "Subject" + or: "o" + subject: "Subjecte" email: "Email" password: "Contrasenya" message: "Missatge" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" + code: "Codi" + ladder: "Escala" + when: "Quan" + opponent: "Oponent" + rank: "Rang" score: "Puntuació" win: "Guanyats" loss: "Perduts" -# tie: "Tie" + tie: "Empat" easy: "Fàcil" medium: "Intermedi" hard: "Difícil" player: "Jugador" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Nivell" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Guerrer" + ranger: "Ranger" + wizard: "Mag" units: second: "segon" @@ -194,90 +216,98 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr play_level: done: "Fet" home: "Inici" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" + level: "Nivell" # Like "Level: Dungeons of Kithgard" skip: "Ometre" game_menu: "Menu de joc" guide: "Guia" restart: "Recomençar" goals: "Objectius" goal: "Objectiu" -# running: "Running..." + running: "Executant..." success: "Exit!" incomplete: "Incomplet" timed_out: "S'ha acabat el temps" failing: "Fallant" action_timeline: "Cronologia d'accions" -# click_to_select: "Click on a unit to select it." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" -# reload_title: "Reload All Code?" -# reload_really: "Are you sure you want to reload this level back to the beginning?" -# reload_confirm: "Reload All" + click_to_select: "Fes clic a una unitat per seleccionar-la" + control_bar_multiplayer: "Multijugador" + control_bar_join_game: "Entrar al joc" + reload: "Recarregar" + reload_title: "Recarregar tot el codi?" + reload_really: "Estàs segur que vos recarregar aquest nivell al principi?" + reload_confirm: "Recarregar tot" + victory: "Victòria" victory_title_prefix: "" victory_title_suffix: " Complet" victory_sign_up: "Inicia sessió per a desar el progressos" -# victory_sign_up_poke: "Want to save your code? Create a free account!" + victory_sign_up_poke: "Vols guardar el teu codi? Crea un compte gratuit!" victory_rate_the_level: "Valora el nivell: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" + victory_return_to_ladder: "Retorna a les Escales" victory_play_continue: "Continuar" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Jugar el següent nivell" - victory_play_more_practice: "Més practica" - victory_play_too_easy: "Massa fàcil" -# victory_play_just_right: "Just Right" - victory_play_too_hard: "Massa difícil" victory_saving_progress: "Desa progrés" victory_go_home: "Tornar a l'inici" # Only in old-style levels. victory_review: "Diguens més!" # Only in old-style levels. -# victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" + victory_hour_of_code_done: "Has acabat?" + victory_hour_of_code_done_yes: "Sí, He acabat amb la meva Hora de Codi™!" + victory_experience_gained: "XP Guanyada" + victory_gems_gained: "Gemmes guanyades" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Guia" -# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. -# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. + tome_minion_spells: "Els encanteris dels Sequaços" # Only in old-style levels. + tome_read_only_spells: "Encanteris de Sola-Lectura" # Only in old-style levels. tome_other_units: "Altres unitats" # Only in old-style levels. tome_cast_button_run: "Executar" tome_cast_button_running: "Executant" tome_cast_button_ran: "Executat" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. + tome_submit_button: "Envia" + tome_reload_method: "Recarrega el codi original code per aquest metòde" # Title text for individual method reload button. tome_select_method: "Selecciona un mètode" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). -# tome_select_a_thang: "Select Someone for " -# tome_available_spells: "Available Spells" + tome_see_all_methods: "Veure tots els mètodes que pots editar" # Title text for method list selector (shown when there are multiple programmable methods). + tome_select_a_thang: "Selecciona Algú Per" + tome_available_spells: "Encanteris disponibles" tome_your_skills: "Les teves habilitats" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" -# skip_tutorial: "Skip (esc)" + tome_help: "Ajuda" + tome_current_method: "Mètode actual" + hud_continue_short: "Continua" + code_saved: "Codi Guardat" + skip_tutorial: "Passar (esc)" keyboard_shortcuts: "Dreceres del teclat" loading_ready: "Preparat!" loading_start: "Comença el nivell" -# problem_alert_title: "Fix Your Code" + problem_alert_title: "Arregla el Teu Codi" + problem_alert_help: "Ajuda" time_current: "Ara:" time_total: "Maxim:" time_goto: "Ves a:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Tornar a intentar" infinite_loop_reset_level: "Reiniciar nivell" -# infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." + infinite_loop_comment_out: "Descommenta el Meu Codi" + tip_toggle_play: "Canvia entre reproduir/pausa amb Ctrl+P" + tip_scrub_shortcut: "Ctrl+[ i Ctrl+] per rebobinar i avançar" # {change} # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." + tip_open_source: "CodeCombat és 100% codi lliure!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat va llançar la seva beta l'octubre de 2013." tip_think_solution: "Pensa en la solució,no en el problema." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" + tip_theory_practice: "En teoria no hi ha diferència entre la teoria i la pràctica. Però a la pràctica si que n'hi ha. - Yogi Berra" + tip_error_free: "Només hi ha dues maneres d'escriure programes sense errors; la tercera és la única que funciona. - Alan Perlis" + tip_debugging_program: "Si debuguejar és el procés d'eliminar errors, llavors programar és el procés de posar-los. - Edsger W. Dijkstra" # tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." + tip_baby_coders: "En el futur fins i tot els bebés podran ser Artximags." # tip_morale_improves: "Loading will continue until morale improves." # tip_all_species: "We believe in equal opportunities to learn programming for all species." # tip_reticulating: "Reticulating spines." # tip_harry: "Yer a Wizard, " # tip_great_responsibility: "With great coding skill comes great debug responsibility." # tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." + tip_binary: "Hi ha 10 tipus de persones al mon, les que saben programar en binari i les que no" # tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" # tip_no_try: "Do. Or do not. There is no try. - Yoda" # tip_patience: "Patience you must have, young Padawan. - Yoda" @@ -289,48 +319,157 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Personalitza el teu bruixot" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventari" save_load_tab: "Desa/Carrega" options_tab: "Opcions" guide_tab: "Gui" + guide_video_tutorial: "Video Tutorial" + guide_tips: "Consells" multiplayer_tab: "Multijugador" -# auth_tab: "Sign Up" + auth_tab: "Dona't d'alta" inventory_caption: "Equipa el teu heroi" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" + choose_hero_caption: "Tria l'heroi, llenguatge" + save_load_caption: "... i veu l'historial" options_caption: "Edita la configuració" guide_caption: "Documents i pistes" multiplayer_caption: "Juga amb amics!" auth_caption: "Desa el progrés." + leaderboard: +# leaderboard: "Leaderboard" + view_other_solutions: "Veure altres solucions" # {change} + scores: "Puntuació" + top_players: "Els millors jugadors de" + day: "Avui" + week: "Aquesta Setmana" + all: "Tots els temps" + time: "Temps" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" + difficulty: "Difficultat" +# gold_collected: "Gold Collected" + inventory: choose_inventory: "Equipar objectes" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + equipped_item: "Equipat" + required_purchase_title: "Necessari" + available_item: "Disponible" + restricted_title: "Restringit" + should_equip: "(doble-clic per equipar)" + equipped: "(equipat)" + locked: "(bloquejat)" + restricted: "(restringit en aquest nivell)" + equip: "Equipa" + unequip: "Desequipa" -# buy_gems: -# few_gems: "A few gems" + buy_gems: + few_gems: "Algunes gemmes" # pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." + chest_gems: "Cofre de gemmes" + purchasing: "Comprant..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" + prompt_body: "En vols més?" + prompt_button: "Entrar a la botiga" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" + feature1: "60+ nivells bàsics a traves de 4 móns" # {change} +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" + feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" + free: "Gratuit" +# month: "month" + subscribe_title: "Subscriu-te" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." + parent_email_input_invalid: "Adreça electrònica invalida." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" + parent_email_send: "Envia correu electrònic" + parent_email_sent: "Correu enviat!" +# parent_email_title: "What's your parent's email?" + parents: "Per Pares" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "Subscripció mensual" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: choose_hero: "Escull el teu heroi" programming_language: "Llenguatge de programació" programming_language_description: "Quin llenguatge de programació vols utilitzar?" -# default: "Default" + default: "Per defecte" # experimental: "Experimental" python_blurb: "Simple però poderós, Python és un bon llenguatge d'us general." javascript_blurb: "El llenguatge de les webs." @@ -339,34 +478,47 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr lua_blurb: "Llenguatge script per a jocs." io_blurb: "Senzill però obscur." status: "Estat" +# hero_type: "Type" weapons: "Armes" -# weapons_warrior: "Swords - Short Range, No Magic" + weapons_warrior: "Espases - Curt abast, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" # weapons_wizard: "Wands, Staffs - Long Range, Magic" attack: "Dany" # Can also translate as "Attack" health: "Salut" speed: "Velocitat" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" + regeneration: "Regeneració" + range: "Abast" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" skills: "Habilitats" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Nomès certs herois poden jugar aquest nivell." -# skill_docs: + skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" + read_only: "Només lectura" + action_name: "nom" # action_cooldown: "Takes" # action_specific_cooldown: "Cooldown" # action_damage: "Damage" -# action_range: "Range" + action_range: "Abast" # action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" + action_duration: "Duracció" + example: "Exemple" + ex: "ex" # Abbreviation of "example" + current_value: "Valor actual" + default_value: "Valor per defecte" + parameters: "Paràmetres" + returns: "Retorna" # granted_by: "Granted by" save_load: @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr volume_label: "Volum" music_label: "Musica" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr press_paragraph_1_link: "paquet de premsa" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." team: "Equip" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" - scott_title: "Programador" + scott_title: "Programador" # {change} # scott_blurb: "Reasonable One" - nick_title: "Programador" + nick_title: "Programador" # {change} # nick_blurb: "Motivation Guru" michael_title: "Programador" # michael_blurb: "Sys Admin" - matt_title: "Programador" + matt_title: "Programador" # {change} # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat per a professors" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Guarda una nova versió" new_major_version: "Nova versió principal" +# submitting_patch: "Submitting Patch..." cla_prefix: "Per guardar els canvis primer has d'acceptar" cla_url: "CLA" cla_suffix: "." cla_agree: "Estic d'acord" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contacta CodeCombat" welcome: "Què bé poder escoltar-te! Fes servir aquest formulari per enviar-nos un email. " - contribute_prefix: "Si estàs interessat en col·laborar, dona un cop d'ull a la nostra " - contribute_page: "pàgina de col·laboració" - contribute_suffix: "!" forum_prefix: "Per a qualsevol publicació, si us plau prova " forum_page: "el nostre fòrum" forum_suffix: " sinó" +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Enviar comentari" contact_candidate: "Contactar amb el candidat" # Deprecated recruitment_reminder: "Utilitza aquest formulari per a contactar amb els candidats que vols entrevistar. Recorda que CodeCombat cobrará el 15% del sou del primer any. El cost es per la contactacio del treballador i es reemborsable durant 90 dies si el treballdor no roman contractat . Temporals, a distancia i treballadors de contracte són gratuits, com els becaris." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr autosave: "Els canvis es guarden automàticament" me_tab: "Jo" picture_tab: "Foto" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" upload_picture: "Carrega una foto" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Contrasenya" emails_tab: "Missatges" admin: "Administrador" new_password: "Contrasenya nova" new_password_verify: "Verifica" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "Subscripcions via correu electrònic" email_subscriptions_none: "Sense subsrcipcions de correu electrònic." email_announcements: "Notícies" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." sample_profile: "Mira un perfil de mostra" view_profile: "Mira el teu perfil" - wizard_tab: "Bruixot" - wizard_color: "Color de la roba" keyboard_shortcuts: keyboard_shortcuts: "Dreceres del teclat" space: "Espai" enter: "Enter" +# press_enter: "press enter" escape: "Escape" shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." - move_wizard: "Mou el teu bruixot pel nivell." community: main_title: "Comunitat CodeCombat" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # social_hipchat: "Chat with us in the public CodeCombat HipChat room" contribute_to_the_project: "Contribueix al projecte" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "Artesà" artisan_title_description: "(Creador de nivells)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "Aventurer" adventurer_title_description: "(Provador de nivells)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Escriba" scribe_title_description: "(Editor d'articles)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "Diplomàtic" diplomat_title_description: "(Traductor)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # thang_title: "Thang Editor" level_title: "Editor de nivells" achievement_title: "Editor de triomfs" +# poll_title: "Poll Editor" back: "Enrere" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" - small: "Petit" + dungeon: "Masmorra" + indoor: "Interior" + desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Petit" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." -# generate_terrain: "Generate Terrain" + generate_terrain: "Generar Terreny" more: "Més" wiki: "Wiki" live_chat: "Xat en directe" + thang_main: "Principal" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" level_tab_scripts: "Scripts" @@ -566,9 +875,14 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr level_tab_thangs_all: "Tot" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" delete: "Esborrar" duplicate: "Duplicar" -# rotate: "Rotate" +# stop_duplicate: "Stop Duplicate" + rotate: "Rotar" level_settings_title: "Configuració" level_component_tab_title: "Components actuals" # level_component_btn_new: "Create New Component" @@ -580,7 +894,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # level_component_edit_title: "Edit Component" # level_component_config_schema: "Config Schema" level_component_settings: "Configuració" -# level_system_edit_title: "Edit System" + level_system_edit_title: "Editar sistema" create_system_title: "Crea un nou sistema" # new_component_title: "Create New Component" new_component_field_system: "Sistema" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr new_level_title_login: "Inicia sessió per a crear un nou nivell" new_achievement_title: "Crea un nou triomf" new_achievement_title_login: "Inicia sessió per a crear un nou triomf" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Vista previa" edit_article_title: "Editar l'article" +# polls: +# priority: "Priority" + contribute: # page_title: "Contributing" - character_classes_title: "Classes de personatges" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" archmage_attribute_1_pref: "Coneixement en " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr fight: "Lluita!" watch_victory: "Mira la teva victòria" defeat_the: "Derrota a" +# tournament_started: ", started" tournament_ends: "El torneig acaba" tournament_ended: "El torneig ha acabat" tournament_rules: "Normes del torneig" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" tournament_blurb_blog: "en el nostre blog" rules: "Normes" winners: "Guanyadors" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr no_achievements: "No has aconseguit cap triomf encara." # favorite_prefix: "Favorite language is " favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." achievements: last_earned: "Últim aconseguit" @@ -770,7 +1077,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr achievement: "Triomf" category_contributor: "Contribuidor" # category_ladder: "Ladder" -# category_level: "Level" + category_level: "Nivell" category_miscellaneous: "Miscel·lània" category_levels: "Nivells" category_undefined: "Sense categoria" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr account: recently_played: "Ultimanent jugat" no_recent_games: "No s'ha jugat en les ultimes setmanes." + payments: "Pagaments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" + service_apple: "Apple" + service_web: "Web" + paid_on: "Pagat a" +# service: "Service" + price: "Preu" +# gems: "Gems" + active: "Actiu" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" + cost: "Cost" + next_payment: "Següent pagament" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "Error de carrega del servidor" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr leaderboard: "Classificació" user_schema: "Esquema d'usuari" user_profile: "Perfil d'usuari" +# patch: "Patch" patches: "Pegats" patched_model: "Document font" model: "Model" @@ -831,23 +1168,50 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # source_document: "Source Document" document: "Documents" # sprite_sheet: "Sprite Sheet" -# employers: "Employers" + employers: "Treballadors" candidates: "Candidats" # candidate_sessions: "Candidate Sessions" # user_remark: "User Remark" # user_remarks: "User Remarks" versions: "Versions" items: "Objectes" +# hero: "Hero" heroes: "Herois" - wizard: "Bruixot" achievement: "Triomf" clas: "CLAs" # play_counts: "Play Counts" feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" delta: added: "Afegit" modified: "Modificat" +# not_modified: "Not Modified" deleted: "Eliminat" moved_index: "Índex desplaçat" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr legal: page_title: "Legalitat" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." privacy_title: "Privacitat" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." security_title: "Seguretat" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." ladder_prizes: title: "Premis del torneig" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr license: "llicencia" # oreilly: "ebook of your choice" - wizard_settings: - title: "Configuració del bruixot" - customize_avatar: "Personalitza el teu avatar" - active: "Actiu" - color: "Color" - group: "Grup" - clothes: "Roba" - trim: "Decoració" - cloud: "Nuvol" - team: "Equip" - spell: "Encanteri" - boots: "Botes" - hue: "Matriu" - saturation: "Saturació" - lightness: "Brillantor" - account_profile: settings: "Configuració" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Modifica el perfil" @@ -1073,7 +1415,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." # deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." # hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. -# get_started: "Get Started" + get_started: "Comença" # already_screened: "We've already technically screened all our candidates" # filter_further: ", but you can also filter further:" # filter_visa: "Visa" @@ -1110,7 +1452,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # candidate_top_skills: "Top Skills" # candidate_years_experience: "Yrs Exp" # candidate_last_updated: "Last Updated" -# candidate_who: "Who" + candidate_who: "Qui" # featured_developers: "Featured Developers" other_developers: "Altres desenvolupadors" # inactive_developers: "Inactive Developers" diff --git a/app/locale/cs.coffee b/app/locale/cs.coffee index 3f3af785d..120698601 100644 --- a/app/locale/cs.coffee +++ b/app/locale/cs.coffee @@ -1,35 +1,36 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", translation: home: - slogan: "Naučte se programování tu při hraní více-hráčové programovací hry." + slogan: "Naučte se programovat hraním hry." no_ie: "Omlouváme se, ale CodeCombat boužel nefunguje v Internet Exploreru 8 nebo starším." # Warning that only shows up in IE8 and older no_mobile: "CodeCombat není navržen pro mobilní zařízení a nemusí fungovat správně!" # Warning that shows up on mobile devices - play: "Hrát" # The big play button that just starts playing a level -# old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari -# old_browser_suffix: "You can try anyway, but it probably won't work." -# campaign: "Campaign" -# for_beginners: "For Beginners" -# multiplayer: "Multiplayer" # Not currently shown on home page -# for_developers: "For Developers" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + play: "Hrát" # The big play button that opens up the campaign view. + old_browser: "Váš prohlížeč je příliš starý na to, aby spustil CodeCombat. Omlouváme se!" # Warning that shows up on really old Firefox/Chrome/Safari + old_browser_suffix: "Můžete to zkusit, ale pravděpodobně to nebude fungovat." + ipad_browser: "Špatné zprávy: CodeCombat neběží na iPad v prohlížeči. Dobré zprávy: naše iPad aplikace čeká na schválení od Apple." + campaign: "Kampaň" + for_beginners: "Pro začátečníky" + multiplayer: "Multiplayer" # Not currently shown on home page + for_developers: "Pro vývojáře" # Not currently shown on home page. + or_ipad: "Nebo stáhnout pro iPad" nav: play: "Úrovně" # The top nav bar entry where players choose which levels to play -# community: "Community" + community: "Komunita" editor: "Editor" blog: "Blog" forum: "Fórum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + account: "Účet" + profile: "Profil" + stats: "Statistiky" + code: "Kód" admin: "Admin" # Only shows up when you are an admin home: "Domů" contribute: "Přispívat" legal: "Licence" - about: "O programu" + about: "O stránkách" contact: "Kontakt" - twitter_follow: "Sledovat na twitteru" -# teachers: "Teachers" + twitter_follow: "Sledovat na Twitteru" + teachers: "Učitelé" modal: close: "Zavřít" @@ -47,356 +48,505 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr subscribe_as_diplomat: "Přihlásit se jako Diplomat" play: -# play_as: "Play As" # Ladder page -# spectate: "Spectate" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + play_as: "Hrát jako" # Ladder page + spectate: "Dívat se" # Ladder page + players: "hráči" # Hover over a level on /play + hours_played: "hodin nahráno" # Hover over a level on /play + items: "Předměty" # Tooltip on item shop button from /play + unlock: "Odemknout" # For purchasing items and heroes + confirm: "Potvrdit" + owned: "Vlastněné" # For items you own + locked: "Zamčené" + purchasable: "Zakoupitelné" # For a hero you unlocked but haven't purchased + available: "Dostupné" + skills_granted: "Nabyté dovednosti" # Property documentation details + heroes: "Hrdinové" # Tooltip on hero shop button from /play + achievements: "Úspěchy" # Tooltip on achievement list button from /play + account: "Účet" # Tooltip on account button from /play + settings: "Nastavení" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "Další" # Go from choose hero to choose inventory before playing a level + change_hero: "Změnit hrdinu" # Go back from choose inventory to choose hero + choose_inventory: "Vyzbrojit se předměty" + buy_gems: "Zakoupit drahokamy" + subscription_required: "Předplatné vyžadováno" + anonymous: "Anonymní hráč" level_difficulty: "Obtížnost: " - campaign_beginner: "Začátečnická úroveň" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Zvolte si úroveň" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Můžete přejít do dalších úrovní, nebo debatovat o úrovních na " - adventurer_forum: "fóru Dobrodruhů" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "...ve které se naučíte kouzla programování." - campaign_dev: "Náhodné težší úrovně" - campaign_dev_description: "...ve kterých se dozvíte více o prostředí při plnění těžších úkolů." + campaign_beginner: "Začátečnická kampaň" + awaiting_levels_adventurer_prefix: "Vypouštíme pět úrovní každý týden." # {change} + awaiting_levels_adventurer: "Přihlašte se jako Dobrodruh" + awaiting_levels_adventurer_suffix: "abyste jako první hráli nejnovější úrovně." + adjust_volume: "Nastavení hlasitosti" campaign_multiplayer: "Multiplayer Aréna" campaign_multiplayer_description: "...ve které programujete proti jiným hráčům." - campaign_player_created: "Uživatelsky vytvořené úrovně" - campaign_player_created_description: "...ve kterých bojujete proti kreativitě ostatních <a href=\"/contribute#artisan\">Zdatných Kouzelníků</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Děláš velké pokroky! Řekni někomu, co jsi se už naučil s CodeCombat." # {change} + email_invalid: "Neplatná e-mailová adresa." + form_blurb: "Vložte jejich e-mail níže a my už je kontaktujeme!" + form_label: "E-mailová adresa" + placeholder: "e-mailová adresa" + title: "Výborná práce, učni" login: sign_up: "Vytvořit účet" log_in: "Přihlásit" -# logging_in: "Logging In" + logging_in: "Přihlašování" log_out: "Odhlásit" - recover: "obnovit účet" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Zapomenuté heslo?" + authenticate_gplus: "Ověřit Google+" + load_profile: "Načíst Google+ Profil" + finishing: "Dokončování" + sign_in_with_facebook: "Přihlásit přes Facebook" + sign_in_with_gplus: "Přihlásit přes Google+" + signup_switch: "Chcete si založit účet?" signup: - create_account_title: "Vytvořit účet k uložení úrovně" - description: "Registrace je zdarma. Vyplňte pouze několik údajů:" email_announcements: "Dostávat novinky emailem" - coppa: "starší 13 let nebo nejste z USA " - coppa_why: "(Proč?)" creating: "Vytvářím účet..." sign_up: "Přihlášení" log_in: "zadejte vaše heslo" -# social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." + social_signup: "Nebo se přihlašte přes Facebook nebo Google+:" + required: "Nejprve se musíte přihlásit." + login_switch: "Máte již účet?" recover: recover_account_title: "Obnovení účtu" send_password: "Zaslat nové heslo" -# recovery_sent: "Recovery email sent." + recovery_sent: "Email s obnovením byl zaslán." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Hlavní" + secondary: "Vedlejší" + armor: "Zbroj" + accessories: "Doplňky" + misc: "Různé" + books: "Spisy" common: + back: "Zpět" # When used as an action verb, like "Navigate backward" + continue: "Pokračovat" # When used as an action verb, like "Continue forward" loading: "Načítání..." saving: "Ukládání..." sending: "Odesílání..." -# send: "Send" + send: "Zaslat" cancel: "Zrušit" save: "Uložit" -# publish: "Publish" -# create: "Create" + publish: "Publikovat" + create: "Vytvořit" manual: "Ručně" fork: "Klonovat" - play: "Přehrát" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + play: "Hrát" # When used as an action verb, like "Play next level" + retry: "Znovu" + actions: "Akce" + info: "Info" + help: "Pomoc" + watch: "Sledovat" + unwatch: "Odsledovat" + submit_patch: "Odeslat opravu" + submit_changes: "Odeslat změny" +# save_changes: "Save Changes" general: and: "a" name: "Jméno" -# date: "Date" + date: "Datum" body: "Tělo" version: "Verze" + pending: "Nevyřízeno" + accepted: "Přijato" + rejected: "Odmítnuto" + withdrawn: "Uzavřeno" + submitter: "Odesílatel" + submitted: "Odesláno" commit_msg: "Popisek ukládání" -# version_history: "Version History" - version_history_for: "Verze historie pro: " -# result: "Result" + review: "Přezkoumání" + version_history: "Seznam změn" + version_history_for: "Seznam změn pro: " + select_changes: "Vyberte dvě změny pro porovnání." + undo_prefix: "Zpět" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Znovu dopředu" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Přehrát náhled současné úrovně" + result: "Výsledek" results: "Výsledky" description: "Popis" or: "nebo" -# subject: "Subject" + subject: "Předmět" email: "Email" -# password: "Password" + password: "Heslo" message: "Zpráva" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + code: "Kód" + ladder: "Žebříček" + when: "Když" + opponent: "Protivník" + rank: "Pořadí" + score: "Skóre" + win: "Vyhrané" + loss: "Prohrané" + tie: "Remízy" + easy: "Jednoduché" + medium: "Střední" + hard: "Těžké" + player: "Hráč" + player_level: "Úroveň" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Válečník" + ranger: "Odstřelovač" + wizard: "Kouzelník" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "sekunda" + seconds: "sekund" + minute: "minuta" + minutes: "minut" + hour: "hodina" + hours: "hodin" + day: "den" + days: "dní" + week: "týden" + weeks: "týdnů" + month: "měsíc" + months: "měsíců" + year: "rok" + years: "roků" play_level: done: "Hotovo" home: "Domů" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "Úroveň" # Like "Level: Dungeons of Kithgard" + skip: "Přeskočit" + game_menu: "Herní menu" guide: "Průvodce" restart: "Restartovat" - goals: "Cíl" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" + goals: "Cíle" + goal: "Cíl" + running: "Běžící..." + success: "Úspěch!" + incomplete: "Nedokončený" + timed_out: "Vypršel čas" + failing: "Selhání" action_timeline: "Časová osa" click_to_select: "Vyberte kliknutím." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" - reload_title: "Znovunačíst veškerý kód?" + control_bar_multiplayer: "Multiplayer" + control_bar_join_game: "Vstoupit do hry" + reload: "Znovu načíst" + reload_title: "Znovu načíst veškerý kód?" reload_really: "Opravdu chcete resetovat tuto úroveň do počátečního stavu?" reload_confirm: "Znovu načíst vše" -# victory_title_prefix: "" + victory: "Vítězství" + victory_title_prefix: "" victory_title_suffix: " Hotovo" victory_sign_up: "Přihlásit se pro uložení postupu" victory_sign_up_poke: "Chcete uložit váš kód? Vytvořte si účet zdarma!" victory_rate_the_level: "Ohodnoťte tuto úroveň: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Hrát další úroveň" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_return_to_ladder: "Vrátit se na Žebříček" + victory_play_continue: "Pokračovat" + victory_saving_progress: "Ukládání postupu" victory_go_home: "Přejít domů" # Only in old-style levels. victory_review: "Připomínky!" # Only in old-style levels. victory_hour_of_code_done: "Skončili jste?" victory_hour_of_code_done_yes: "Ano, pro dnešek jsem skončil!" + victory_experience_gained: "Získáno zkušeností" + victory_gems_gained: "Získáno drahokamů" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Průvodce" tome_minion_spells: "Vaše oblíbená kouzla" # Only in old-style levels. tome_read_only_spells: "Kouzla jen pro čtení" # Only in old-style levels. tome_other_units: "Ostatní jednotky" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_run: "Spustit" + tome_cast_button_running: "Spuštěné" + tome_cast_button_ran: "Spuštěno" + tome_submit_button: "Odeslat" + tome_reload_method: "Znovu načíst původní kód pro tuto metodu" # Title text for individual method reload button. + tome_select_method: "Vybrat metodu" + tome_see_all_methods: "Vybrat všechny metody, které mohou být upraveny" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Zvolte někoho pro " tome_available_spells: "Dostupná kouzla" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." -# tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." -# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Upravit Kouzelníka" + tome_your_skills: "Vaše dovednosti" + tome_help: "Pomoc" + tome_current_method: "Aktuální metoda" + hud_continue_short: "Pokračovat" + code_saved: "Kód uložen" + skip_tutorial: "Přeskočit (esc)" + keyboard_shortcuts: "Klávesové zkratky" + loading_ready: "Připraven!" + loading_start: "Začít úroveň" + problem_alert_title: "Oprav si kód" + problem_alert_help: "Pomoc" + time_current: "Nyní:" + time_total: "Max:" + time_goto: "Jít na:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "Zkusit znovu" + infinite_loop_reset_level: "Resetovat úroveň" + infinite_loop_comment_out: "Zakomentovat můj kód" + tip_toggle_play: "Přepněte přehrávání/pauzu pomocí Ctrl+P." + tip_scrub_shortcut: "Ctrl+[ a Ctrl+] pro přetočení a rychlý přesun." # {change} + tip_guide_exists: "Klikněte na průvode uvnitř herního menu (nahoře na stránce), pro užitečné informace." + tip_open_source: "CodeCombat je 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat spustil svoji beta verzi v Říjnu, 2013." + tip_think_solution: "Myslete na řešení, ne na problém." + tip_theory_practice: "Teoreticky není žádný rozdíl mezi teorií a praxí. Ale v praxi ten rozdíl je. - Yogi Berra" + tip_error_free: "Jsou dva způsoby jak psát programy bez chyb; jenom ten třetí funguje. - Alan Perlis" + tip_debugging_program: "Pokud je ladění proces, při kterém se odstraňují chyby, programování tedy musí být proces, kterým se chyby přidávají. - Edsger W. Dijkstra" + tip_forums: "Jděte na naše fóra a řekněte nám, co si myslíte!" + tip_baby_coders: "V budoucnu budou i mimina Arcikouzelníky." + tip_morale_improves: "Načítání bude probíhat, dokud se nezlepší morálka." + tip_all_species: "Věříme ve stejné možnosti učení se programovat pro všechny druhy." + tip_reticulating: "Síťování páteří." + tip_harry: "Hej kouzelníku, " + tip_great_responsibility: "S velkými dovednosti v programování přichází velká odpovědnost ladění." + tip_munchkin: "Pokud nebudeš jíst zeleninu, munchkin půjde po tobě mezitím co budeš spát." + tip_binary: "Je jenom 10 typů lidí na tomto světě: ti, kteří rozumí binání soustavě a ti, kteří jí nerozumí." + tip_commitment_yoda: "Programátor musí mít nejhlubší oddanost, nejvážnější mysl. - Yoda" + tip_no_try: "Dělej. Nebo nedělej. Avšak nikdy nezkoušej. - Yoda" + tip_patience: "Trpělivost musíš mít, mladý Padawan. - Yoda" + tip_documented_bug: "Zadokumentovaná chyba není chyba; je to funkce." + tip_impossible: "Vždy se to zdá nemožné dokud to není dokončeno. - Nelson Mandela" + tip_talk_is_cheap: "Mluvení je pro slabé. Ukaž mi kód. - Linus Torvalds" + tip_first_language: "Nejvíce zničující věc, kterou se můžeš naučit je tvůj první programovací jazyk. - Alan Kay" + tip_hardware_problem: "Q: Kolik programátorů je potřeba k výměně žárovky? Žádný. To je problém hardwaru." + tip_hofstadters_law: "Hofstadterův zákon: Vše vždy trvá déle, než očekáváme, a to i když bereme v úvahu Hofstadterův zákon." + tip_premature_optimization: "Předčasná optimalizace je původce všeho zla. - Donald Knuth" + tip_brute_force: "V případě pochybností, použijte brute force. - Ken Thompson" + tip_extrapolation: "Jsou jenom dva druhy lidí: ti, kteří mohou extrapolovat z nekompletních dat..." + tip_superpower: "Kódování by se téměř dalo srovnávat se superschopnostmi." + tip_control_destiny: "V opravdovém open source máte právo řídit svůj osud. - Linus Torvalds" + tip_no_code: "Žádný kód není rychlejší než žádný kód." + tip_code_never_lies: "Kód nikdy nelže, komentáře někdy ano. — Ron Jeffries" + tip_reusable_software: "Předtím, než může být software znovu použitelný, musí být nejprve použitelný." + tip_optimization_operator: "Každý jazyk má své optimalizace. Ve většině jazyku to je \"//\"" + tip_lines_of_code: "Měření postupu v programování podle počtu řádků je jako měření postupu stavby letadla podle váhy. — Bill Gates" + tip_source_code: "Chci změnit svět, ale nechtějí mě dát k němu zdrojový kód." + tip_javascript_java: "Porovnávat Javu a JavaScript je stejné, jako porovnávat auto a autoritu. - Chris Heilmann" + tip_move_forward: "Ať už děláš cokoliv, vždy jdi dopředu. - Martin Luther King Jr." + tip_google: "Máš problém, který nemůžeš vyřešit? Vygoogluj to!" + tip_adding_evil: "Přidávání špetky zla." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "Inventář" + save_load_tab: "Uložit/Načíst" + options_tab: "Možnosti" + guide_tab: "Průvodce" + guide_video_tutorial: "Video Tutoriál" + guide_tips: "Tipy" multiplayer_tab: "Multiplayer" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + auth_tab: "Registrovat se" + inventory_caption: "Vybavte svého hrdinu" + choose_hero_caption: "Vyberte hrdinu, jazyk" + save_load_caption: "... a prohledněte si historii" + options_caption: "Konfigurace nastavení" + guide_caption: "Dokumentace a tipy" + multiplayer_caption: "Hrajte s přáteli!" + auth_caption: "Uložte váš postup." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Žebříčky" + view_other_solutions: "Zobrazit jiné řešení" # {change} + scores: "Skóre" +# top_players: "Top Players by" + day: "Dnes" + week: "Tento týden" + all: "Celkově" + time: "Čas" + damage_taken: "Obdrženo zranění" + damage_dealt: "Uděleno zranění" + difficulty: "Obtížnost" + gold_collected: "Sebráno zlata" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "Nasadit předměty" + equipped_item: "Nasazeno" + required_purchase_title: "Vyžadováno" + available_item: "Dostupné" + restricted_title: "Omezeno" + should_equip: "(dvojklik pro nasazení)" + equipped: "(nasazeno)" + locked: "(zamčeno)" + restricted: "(omezeno v této úrovni)" + equip: "Nasadit" + unequip: "Sundat" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + buy_gems: + few_gems: "Pár drahokamů" + pile_gems: "Hromada drahokamů" + chest_gems: "Truhlice drahokamů" + purchasing: "Probíhá nákup..." + declined: "Vaše karta byla odmítnuta" + retrying: "Chyba serveru, obnovování." + prompt_title: "Nedostatek drahokamů" + prompt_body: "Chcete získat více?" + prompt_button: "Vstoupit do obchodu" + recovered: "Obnovení již zakoupených drahokamů proběhlo úspěšně. Aktualizujte stránku prosím." +# price: "x3500 / mo" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + subscribe: + comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" + feature1: "60+ základních úrovní napříč 4 světy" # {change} + feature2: "7 silných <strong>nových hrdinů</strong> s jedinečnými dovednostmi!" # {change} + feature3: "30+ bonusových úrovní" # {change} + feature4: "<strong>3500 bonusových drahokamů</strong> každý měsíc!" + feature5: "Video tutoriály" + feature6: "Premiová e-mailová podpora" +# feature7: "Private <strong>Clans</strong>" + free: "Zdarma" + month: "měsíc" + subscribe_title: "Předplatné" + unsubscribe: "Zrušit předplatné" + confirm_unsubscribe: "Potvrdit zrušení" + never_mind: "To nevadí, pořád tě miluji" + thank_you_months_prefix: "Děkujeme za vaši podporu v posledních" + thank_you_months_suffix: "měsících." + thank_you: "Děkujeme za podporu CodeCombatu." + sorry_to_see_you_go: "To je škoda! Dejte nám prosím vědět, co můžeme udělat lépe." + unsubscribe_feedback_placeholder: "Co jsme spolu dokázali?" + parent_button: "Zeptat se rodičů" + parent_email_description: "Napíšeme jim e-mail, aby ti mohli koupit CodeCombat předplatné." + parent_email_input_invalid: "Neplatná e-mailová adresa." + parent_email_input_label: "E-mailová adresa rodičů" + parent_email_input_placeholder: "Napiš e-mail rodičů" + parent_email_send: "Odeslat e-mail" + parent_email_sent: "E-mail odeslán!" + parent_email_title: "Jaký je e-mail tvých rodičů?" + parents: "Pro rodiče" + parents_title: "Vaše dítě se naučí programovat." # {change} + parents_blurb1: "Pomocí CodeCombat se vaše dítě učí psaním opravdového kódu. Začínají učením se základním příkazů a postupně se přidávají pokročilejší témata." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "Za $9.99 USD/měsíc, získají nové výzvy každý týden a osobní emailovou podporu od profesionálních programátorů." # {change} + parents_blurb3: "Bez rizika: 100% záruka vrácení peněz, jednoduché zrušení předplatného na 1 kliknutí." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "Měsíční předplatné" + subscription_required_to_play: "Pro hraní této úrovně potřebujete předplatné." + unlock_help_videos: "Kupte si předplatné pro odemčení všech tutoriálových videí." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + choose_hero: + choose_hero: "Vyberte vašeho hrdinu" + programming_language: "Programovací jazyk" + programming_language_description: "Jaký programovací jazyk chcete použít?" + default: "Výchozí" + experimental: "Pokusný" + python_blurb: "Jednoduchý byť silný, dobrý pro začátečníky i pokročilé." + javascript_blurb: "Jazyk webových stránek. (Není stejný jako Java.)" + coffeescript_blurb: "Hezčí JavaScript syntaxe." + clojure_blurb: "Moderní Lisp." + lua_blurb: "Jazyk pro skriptování her." + io_blurb: "Jednoduchý ale nejasný." + status: "Stav" +# hero_type: "Type" + weapons: "Zbraně" + weapons_warrior: "Meče - Krátká vzdálenost, Žádná magie" + weapons_ranger: "Kuše, Zbraně - Dlouhá vzdálenost, Žádná magie" + weapons_wizard: "Hole, Tyče - Dlouhá vzdálenost, Magie" + attack: "Poškození" # Can also translate as "Attack" + health: "Zdraví" + speed: "Rychlost" + regeneration: "Regenerace" + range: "Vzdálenost" # As in "attack or visual range" + blocks: "Blokuje" # As in "this shield blocks this much damage" + backstab: "Zapíchnutí do zad" # As in "this dagger does this much backstab damage" + skills: "Dovednosti" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." + available_for_purchase: "Dostupné pro zakoupení" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Úrovně pro odemknutí:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Pouze určití hrdinové mohou hrát tuto úroveň." -# options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." -# editor_config: "Editor Config" -# editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" -# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." + skill_docs: + writable: "zapisovatelná" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "jen pro čtení" + action_name: "název" + action_cooldown: "Zabere" + action_specific_cooldown: "Cooldown" + action_damage: "Poškození" + action_range: "Vzdálenost" + action_radius: "Dosah" + action_duration: "Trvání" + example: "Příklad" + ex: "př." # Abbreviation of "example" + current_value: "Aktuální hodnota" + default_value: "Výchozí hodnota" + parameters: "Parametry" + returns: "Vrací" + granted_by: "Poskytnutné od" + + save_load: + granularity_saved_games: "Uložené" + granularity_change_history: "Historie" + + options: + general_options: "Obecné nastavení" # Check out the Options tab in the Game Menu while playing a level + volume_label: "Hlasitost" + music_label: "Hudba" + music_description: "Vypnout/zapnout hudbu v pozadí." + editor_config: "Konfigurovat editor" + editor_config_title: "Konfigurace editoru" + editor_config_level_language_label: "Jazyky pro tuto úroveň" + editor_config_level_language_description: "Vybrat programovací jazyk pro tuto úroveň." + editor_config_default_language_label: "Výchozí programovací jazyk" + editor_config_default_language_description: "Zvolte programovací jazyk, ve kterém chcete kódovat nové úrovně." + editor_config_keybindings_label: "Klávesové zkratky" + editor_config_keybindings_default: "Výchozí" + editor_config_keybindings_description: "Přidává další zkratky známé od běžných editorů." + editor_config_livecompletion_label: "Živé automatické dokončování" + editor_config_livecompletion_description: "Ukáže návrhy automatického doplňování během psaní." + editor_config_invisibles_label: "Zobrazit neviditelné" + editor_config_invisibles_description: "Ukáže neviditelné jako mezery atd." + editor_config_indentguides_label: "Zobrazit odsazení" + editor_config_indentguides_description: "Ukáže vertikální čáry pro jednoduché vidění odsazení." + editor_config_behaviors_label: "Chytré chování" + editor_config_behaviors_description: "Automaticky doplňovat závorky a uvozovky." about: why_codecombat: "Proč CodeCombat?" @@ -407,42 +557,136 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr why_paragraph_2_italic_caps: "POČKEJ MAMI, MUSÍM DOKONČIT ÚROVEŇ!" why_paragraph_2_suffix: "Proto je CodeCombat opravdová multiplayer hra, ne lekce kurzu s herními odznáčky. Neskončí, dokud sami nepřestanete, což je tentokrát dobrá věc." why_paragraph_3: "A jestli se máte stát závislými na nějaké hře, pak ať je to hra tato, a staňte se díky tomu kouzelníky a odborníky v této technické době." -# press_title: "Bloggers/Press" -# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" -# press_paragraph_1_link: "press packet" -# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + press_title: "Blogeři/Tisk" + press_paragraph_1_prefix: "Chcete o nás napsat? Můžete si stáhnout a použít všechny naše zdroje zahrnuté v našem" + press_paragraph_1_link: "balíčku pro tisk" + press_paragraph_1_suffix: ". Všechny loga a obrázky mohou být použity bez toho, abyste nás museli přímo kontaktovat." + team: "Tým" + george_title: "Výkonný ředitel" # {change} + george_blurb: "Obchodník" + scott_title: "Programátor" # {change} + scott_blurb: "Ten potřebný" + nick_title: "Programátor" # {change} + nick_blurb: "Motivační guru" + michael_title: "Programátor" + michael_blurb: "Systémový administrátor" + matt_title: "Programátor" # {change} + matt_blurb: "Cyklista" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat pro učitele" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." + sys_requirements_title: "Systemové požadavky" + sys_requirements_1: "Protože CodeCombat je hra, pro počítače je to více náročnější než přehrávání videa. Optimalizovali jsme CodeCombat, aby běžel rychle na všech moderních prohlížečích a dokonce i na starších mašinách, takže každý může hrát. Takže tady jsou naše návrhy, jak si co nejvíce užít váš Hour of Code zážitek:" # {change} + sys_requirements_2: "Používejte nejnovější verzi Chromu nebo Firefoxu." # {change} + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: - save_version_title: "Uložit novou Verzi" - new_major_version: "Nová hlavní Verze" + save_version_title: "Uložit novou verzi" + new_major_version: "Nová hlavní verze" + submitting_patch: "Odesílání opravy..." cla_prefix: "Před uložením musíte souhlasit s" cla_url: "licencí" cla_suffix: "." cla_agree: "SOUHLASÍM" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Konktujte CodeCombat" welcome: "Rádi od vás uslyšíme. Použijte tento formulář pro odeslání emailu. " - contribute_prefix: "Chcete-li nám přispět, prohlédněte si naši stránku " - contribute_page: "přispivatelů" - contribute_suffix: "!" forum_prefix: "Pro ostatní veřejné věci, prosím zkuste " forum_page: "naše fórum" forum_suffix: "." + faq_prefix: "Také máme " + faq: "FAQ" + subscribe_prefix: "Pokud potřebujete pomoc s nějakou úrovní, prosím" + subscribe: "zakupte si CodeCombat předplatné" + subscribe_suffix: "a rádi vám pomůžeme s vaším kódem." + subscriber_support: "Již jste CodeCombat předplatitel, takže vaše emaily budou vyřízeny dříve." + screenshot_included: "Snímek obrazovky zahrnut." + where_reply: "Kam máme odpovědět?" send: "Odeslat připomínku" -# contact_candidate: "Contact Candidate" # Deprecated -# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated + contact_candidate: "Kontaktovat kandidáta" # Deprecated + recruitment_reminder: "Použijte tento formulář pro kontaktování kandidátů, se kterými chcete udělat rozhovor. Pamatujte, že CodeCombat si účtuje 15% z platu po dobu prvního roku. Tento poplatek je splatný při náboru zaměstancnů a je vratný do 90 dní pokud zaměstnanec nezůstane zaměstnán. Zaměstnanci na částečný úvazek, dálkový a zaměstnanci se smlouvami jsou zdarma, stejně jako stážisté." # Deprecated account_settings: title: "Nastavení účtu" @@ -450,23 +694,30 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr autosave: "Automatické ukládání změn" me_tab: "O mne" picture_tab: "Obrázek" -# upload_picture: "Upload a picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" + upload_picture: "Nahrát obrázek" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Heslo" emails_tab: "Emaily" -# admin: "Admin" + admin: "Admin" new_password: "Nové heslo" new_password_verify: "Potvrdit" - email_subscriptions: "Doručovat emailem" -# email_subscriptions_none: "No Email Subscriptions." +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." + email_subscriptions: "Odebírat emailem" + email_subscriptions_none: "Žádné odebírání emailem." email_announcements: "Oznámení" email_announcements_description: "Zasílat emaily o posledních novinkách a o postupu ve vývoji CodeCombat." -# email_notifications: "Notifications" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." + email_notifications: "Upozornění" + email_notifications_summary: "Ovládání osobních, automatických emailových upozornění o vaší CodeCombat aktivitě." + email_any_notes: "Jakékoliv upozornění" + email_any_notes_description: "Vypněte pro zastavení všech emailů spojených s upozorněními o aktivitě." + email_news: "Novinky" + email_recruit_notes: "Pracovní příležitosti" + email_recruit_notes_description: "Pokud budete hrát velmi dobře, můžeme vás kontaktovat a nabídnou vám (lepší) práci." contributor_emails: "Emaily pro přispívatele" contribute_prefix: "Hledáme další přispívatele! Čtěte prosím " contribute_page: "stránku přispívatelům" @@ -475,100 +726,163 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr error_saving: "Chyba při ukládání" saved: "Změny uloženy" password_mismatch: "Hesla nesouhlasí." -# password_repeat: "Please repeat your password." -# job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated -# job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." -# job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - wizard_tab: "Kouzelník" - wizard_color: "Barva Kouzelníkova oblečení" + password_repeat: "Opakujte prosím vaše heslo." + job_profile: "Pracovní profil" # Rest of this section (the job profile stuff and wizard stuff) is deprecated + job_profile_approved: "Váš pracovní profil byl schválen. Zaměstnavatelé ho uvidí do doby, než ho neoznačíte jako neaktivní nebo nebude změněn po dobu 4 týdnů." + job_profile_explanation: "Ahoj! Vyplň tohle a kontaktujeme tě ohledně nalezení práce jako vývojář softwaru." + sample_profile: "Podívat se na ukázkový profil" + view_profile: "Podívat se na profil" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." + keyboard_shortcuts: + keyboard_shortcuts: "Klávesové zkratky" + space: "Mezerník" + enter: "Enter" +# press_enter: "press enter" + escape: "Escape" + shift: "Shift" + run_code: "Spustit současný kód." + run_real_time: "Spustit v reálném čase." + continue_script: "Pokračovat v současném skriptu." + skip_scripts: "Přeskočit všechny přeskočitelné skripty." + toggle_playback: "Přepnout přehrávání/pauzu." + scrub_playback: "Procházet zpět a vpřed skrz čas." + single_scrub_playback: "Procházet zpět a vpřed skrz čas po jednotlivých snímcíche." + scrub_execution: "Procházet skrz stávající provádění kouzla." + toggle_debug: "Přepnout displej ladění." + toggle_grid: "Přepnout překrytí mřížkou." + toggle_pathfinding: "Přepnout překrytí nalezání cesty." + beautify: "Udělejte váš kod hezčí standardizováním jeho formátu." + maximize_editor: "Maximalizovat/minimalizovat editor kódu." -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + community: + main_title: "CodeCombat Komunita" + introduction: "Koukněte se na způsoby, jak se můžete zapojit níže a rozhodněte se co vám nejvíce vyhovuje. Těšíme se na pracování s vámi!" + level_editor_prefix: "Použít CodeCombat" + level_editor_suffix: "pro vytvoření a upravení úrovní. Uživatelé vytvořili úrovně pro jejich třídy, přátele, hackatony, studenty, a sourozence. Pokud se bojít vytváření nové úrovně, můžete začít Klonováním jednoho z našich!" + thang_editor_prefix: "Jednotky ve hře nazýváme 'thangy'. Použij" + thang_editor_suffix: "pro upravení CodeCombat grafiky. Povol jednotkám házet projektily, změň směr animací, změň body zásahů jednotek nebo nahraj vlastní grafické návrhy." + article_editor_prefix: "Vidíš nějakou chybu v naší dokumentaci? Chcete vytvořit instrukce pro vlastní tvorbu? Podívejte se na" + article_editor_suffix: "a pomožte CodeCombat hráčům dostat co nejvíce z jejich stráveného času." + find_us: "Najdete nás na těchto stránkách" + social_blog: "Přečtěte si CodeCombat blog na Sett" + social_discource: "Vstupte do diskuze na našem Discourse fóru" + social_facebook: "Dejte Like CodeCombat na Facebooku" + social_twitter: "Sledujte CodeCombat na Twitteru" + social_gplus: "Připojte se k CodeCombat na Google+" + social_hipchat: "Chatujte s námi ve veřejné CodeCombat HipChat místnosti" + contribute_to_the_project: "Přispějte tomuto projektu" + +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" classes: archmage_title: "Arcikouzelník" archmage_title_description: "(Programátor)" + archmage_summary: "Pokud jste vývojář se zájmem o kódování vzdělávacích her, staňte se Arcikouzelníkem, abyste nám mohli pomoct s vývojem CodeCombat!" artisan_title: "Řemeslník" artisan_title_description: "(Tvůrce úrovní)" + artisan_summary: "Stavte a sdílejte úrovně, abyste si na nich mohli zhrát i s přáteli. Staňte se Řemeslníkem, abyste se učili umění učit jiné programovat." adventurer_title: "Dobrodruh" adventurer_title_description: "(Tester úrovní)" - scribe_title: "Pisálek" + adventurer_summary: "Dostaňte nové úrovně (dokonce i obsah pro předplatitele) zdarma s týdenním předstihem a pomozte nám vyřešit chyby před vydáním." + scribe_title: "Písař" scribe_title_description: "(Editor článků)" + scribe_summary: "Dobrý kód potřebuje dobrou dokumentaci. Pište, upravuje a vylepšujte dokumentaci, kterou čtou miliony hráčů po celé zemi." diplomat_title: "Diplomat" diplomat_title_description: "(Překladatel)" + diplomat_summary: "CodeCombat je přeložen do 45+ jazyků našimi Diplomaty. Pomozte nám a přispějte se svými překlady." ambassador_title: "Velvyslanec" ambassador_title_description: "(Podpora)" + ambassador_summary: "Kroťte naše uživatele na fóru a poskytujte odpovědi těm, kteří se ptají. Naši Velvyslanci reprezentují CodeCombat ve světě." editor: main_title: "Editory CodeCombatu" article_title: "Editor článků" thang_title: "Editor Thangů - objektů" level_title: "Editor úrovní" -# achievement_title: "Achievement Editor" -# back: "Back" -# revert: "Revert" -# revert_models: "Revert Models" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." -# generate_terrain: "Generate Terrain" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" + achievement_title: "Editor Úspěchů" +# poll_title: "Poll Editor" + back: "Zpět" + revert: "Vrátit" + revert_models: "Vrátit modely" + pick_a_terrain: "Vybrat terén" + dungeon: "Dungeon" + indoor: "Úkryt" + desert: "Poušť" + grassy: "Travnatý" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Malý" + large: "Velký" + fork_title: "Forkovat novou verzi" + fork_creating: "Vytváření Forku..." + generate_terrain: "Generování terénu" + more: "Vícee" + wiki: "Wiki" + live_chat: "Živý Chat" + thang_main: "Hlavní" + thang_spritesheets: "Spritesheety" + thang_colors: "Barvy" level_some_options: "Volby?" level_tab_thangs: "Thangy" level_tab_scripts: "Skripty" level_tab_settings: "Nastavení" level_tab_components: "Komponenty" level_tab_systems: "Systémy" -# level_tab_docs: "Documentation" + level_tab_docs: "Dokumentace" level_tab_thangs_title: "Současné Thangy" -# level_tab_thangs_all: "All" + level_tab_thangs_all: "Všechny" level_tab_thangs_conditions: "Výchozí prostředí" level_tab_thangs_add: "Přidat Thangy" -# delete: "Delete" -# duplicate: "Duplicate" -# rotate: "Rotate" +# level_tab_thangs_search: "Search thangs" + add_components: "Přidat součásti" + component_configs: "Nastavení součástí" + config_thang: "Dvoj-klik pro konfiguraci thangu" + delete: "Smazat" + duplicate: "Duplikovat" + stop_duplicate: "Zastavit duplikování" + rotate: "Otočit" level_settings_title: "Nastavení" level_component_tab_title: "Současné komponenty" level_component_btn_new: "Vytvořit novou komponentu" @@ -578,47 +892,50 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr level_components_title: "Zpět na všechny Thangy" level_components_type: "Druh" level_component_edit_title: "Editovat komponentu" -# level_component_config_schema: "Config Schema" -# level_component_settings: "Settings" + level_component_config_schema: "Konfigurační schéma" + level_component_settings: "Nastavení" level_system_edit_title: "Editovat systém" create_system_title: "Vytvořit nový systém" new_component_title: "Vytvořit novou komponentu" new_component_field_system: "Systém" -# new_article_title: "Create a New Article" -# new_thang_title: "Create a New Thang Type" -# new_level_title: "Create a New Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" -# article_search_title: "Search Articles Here" -# thang_search_title: "Search Thang Types Here" -# level_search_title: "Search Levels Here" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" -# pop_i18n: "Populate I18N" + new_article_title: "Vytvořit nový článek" + new_thang_title: "Vytvořit nový typ Thangu" + new_level_title: "Vytvořit novou úroveň" + new_article_title_login: "Přihlašte se pro vytvoření nového článku" + new_thang_title_login: "Přihlašte se pro vytvoření nového typu Thangu" + new_level_title_login: "Přihlašte se pro vytvoření nové úrovně" + new_achievement_title: "Vytvořit nový Úspěch" + new_achievement_title_login: "Přihlašte se pro vytvoření nového Úspěchu" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" + article_search_title: "Vyhledat články" + thang_search_title: "Vyhledat typy Thangů" + level_search_title: "Vyhledat úrovně" + achievement_search_title: "Hledat Úspěchy" +# poll_search_title: "Search Polls" + read_only_warning2: "Poznámka: nemůžete ukládat žádné změny, protože nejste přihlášeni." + no_achievements: "Pro tuto úroveň ještě nebyly přidány úspěchy." + achievement_query_misc: "Klíčové úspěchy ostatních" + achievement_query_goals: "Klíčové úspěchy cílů úrovní" + level_completion: "Dokončení úrovně" + pop_i18n: "Osídlit I18N" + tasks: "Úkoly" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Náhled" edit_article_title: "Editovat článek" +# polls: +# priority: "Priority" + contribute: page_title: "Přispívání" - character_classes_title: "Obsazení rolí" - introduction_desc_intro: "Vkládáme do CodeCombatu velké naděje." - introduction_desc_pref: "Chceme být to místo, ve kterém se všichni programátoři sejdou pro společnou hru a učení, uvedou další do úžasného světa programování a kde se předvede elita. Víme, že toto sami nezvládneme, jsou to lidé, kteří dělají projekty typu GitHub, Stack Overflow a Linux úspěšnými. Za tímto účelem, " - introduction_desc_github_url: "CodeCombat je kompletně open source" - introduction_desc_suf: "a snažíme se jak jen to jde, abychom vám umožnili se do tohoto projektu zapojit." - introduction_desc_ending: "Doufáme, že se k nám přidáte!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy a Matt" + intro_blurb: "CodeCombat je 100% open source! Stovky nadšených hráčů již pomohli postavit hru do podoby, kterou vidíte dnes. Připojte se a napište další kapitolu CodeCombatu, ve kterém se učí celý svět kódovat!" alert_account_message_intro: "Vítejte!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." + alert_account_message: "Pro odebírání třídních emailů musíte být nejdříve přihlášeni." archmage_introduction: "Jedna z nejlepších věcí na vytváření her je to, že se jedná o spojení různých procesů. Grafika, zvuk, síťování v reálném čase, mezilidské vztahy a samozřejmě také spousta běžných aspektů programování, od nízkoúrovňového managementu databáze přes administraci serverů až po tvorbu uživatelská rozhraní. Je zde spousta práce a pokud jste zkušený programátor a všeuměl připravený k ponoření se do hloubek CodeCombatu, tato skupina je pro vás. Budeme moc rádi za vaši pomoc při tvorbě té nejlepší programovací hry." class_attributes: "Vlastnosti" archmage_attribute_1_pref: "Znáte " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr join_desc_4: "!" join_url_email: "Pošlete nám email" join_url_hipchat: "veřejné HipChat chatovací místnosti" - more_about_archmage: "Dozvědět se více o tom, jak se stát mocným Arcimágem" archmage_subscribe_desc: "Dostávat emailem oznámení a informacemi nových programovacích příležitostech" -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." artisan_introduction_pref: "Musíme vytvářet další úrovně! Lidé nás prosí o další obsah, ale sami zvládáme vytvořit jen málo. Naším prvním pracovním zastavením je první úroveň. Editor úrovní je tak-tak použitelný i pro jeho vlastní tvůrce. Máte-li vizi pro vytvoření vnořených úrovní alá " artisan_introduction_suf: "pak neváhejte, toto je vaše destinace." artisan_attribute_1: "Předchozí zkušenosti s vytvářením podobného obsahu by byly vítány, například z editorů úrovní Blizzardu, ale nejsou vyžadovány!" @@ -645,47 +959,37 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr artisan_join_step2: "Vytvořte novou úroveň a prozkoumejte existující úrovně." artisan_join_step3: "Požádejte nás o pomoc ve veřejné HipChat místnosti." artisan_join_step4: "Zveřejněte vaši úroveň na fóru pro připomínkování." - more_about_artisan: "Dozvědět se více o tom, jak se stát kreativním Řemeslníkem" artisan_subscribe_desc: "Dostávat emailem oznámení a informace o aktualizacích editoru úrovní." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." adventurer_introduction: "Ujasněme si dopředu jednu věc o vaší roli: budete jako tank. Projdete ohněm. Potřebujeme někoho, kdo odzkouší zbrusu nové úrovně a pomůže identifikovat kde je možno je zlepšit. Ten boj bude ohromný - tvorba her je dlouhý proces, který nikdo nezvládne na první pokus. Máte-li na to a vydržíte-li to, pak toto je vaše skupina." adventurer_attribute_1: "Touha po učení se. Vy se chcete naučit programovat a my vás to chceme naučit. Jenom, v tomto případě to budete vy, kdo bude vyučovat." adventurer_attribute_2: "Charismatický. Buďte mírný a pečlivě artikulujte co a jak je potřeba zlepšit." adventurer_join_pref: "Buďto se spojte (nebo si najděte!) Dobrodruha a pracujte s ním, nebo zaškrtněte políčko níže a dostávejte emaily o dostupnosti nových úrovní k testování. Budeme také posílat informace o nových úrovních k recenzím na sociálních webech, " adventurer_forum_url: " našem fóru" adventurer_join_suf: "takže pokud chcete být v obraze, připojte se k nám!" - more_about_adventurer: "Dozvědět se více o tom, jak se stát statečným Dobrodruhem" adventurer_subscribe_desc: "Dostávat emailem oznámení a informace nových úrovních k testování." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." scribe_introduction_pref: "CodeCombat nebude pouze kupa úrovní. Bude také zahrnovat informační zdroje a wiki programovacích konceptů na které se úrovně mohou navázat. Takto, namísto toho aby každý Řemeslník musel sám do detailu popsatco který operátor dělá, mohou jednoduše nalinkovat svoji úroveň na článek existující k edukaci hráčů. Něco ve stylu " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: ". Jestliže vás baví popisovat a předávat koncept programování v Markdown editoru, pak tato role může být právě pro vás." scribe_attribute_1: "Zkušenost s psaním je jediné co budete potřebovat. Nejen gramatika, ale také schopnost popsat složité koncepty a myšlenky ostatním." contact_us_url: "Spojte se s námi" scribe_join_description: "dejte nám o vás vědět, o vašich zkušenostech s programováním a o čm byste rádi psali. Od toho začneme!" - more_about_scribe: "Dozvědět se více o tom, jak se stát pilným Pisálkem" scribe_subscribe_desc: "Dostávat emailem oznámení a informace o článcích." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." diplomat_introduction_pref: "Jedna z věcí, kterou jsme zjistili během " diplomat_launch_url: "zahájení v Říjnu" diplomat_introduction_suf: "bylo, že o CodeCombat je velký zájem i v jiných zemích, obzvláště v Brazílii! Chystáme regiment překladatelů ke zpřístupnění CodeCombatu světu. Pokud chcete nakouknout pod pokličku, dozvědět se o připravovaných novinkách a zpřístupnit úrovně vašim národním kolegům, toto je role pro vás." - diplomat_attribute_1: "Plynulost v angličtině a v jazyce do kterého budete překládat. Při předávání komplexních myšlenek je důležité si být jistí v kramflecích v obou jazycích!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." -# diplomat_join_pref_github: "Find your language locale file " -# diplomat_github_url: "on GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" - more_about_diplomat: "Dozvědět se více o tom, jak se stát Diplomatem" + diplomat_attribute_1: "Plynulost v angličtině a v jazyce do kterého budete překládat. Při předávání komplexních myšlenek je důležité si být jisti výrazy v obou jazycích!" + diplomat_i18n_page_prefix: "Můžete začít překládat naše úrovně přejítím na naše" + diplomat_i18n_page: "stránky překladů" + diplomat_i18n_page_suffix: ", nebo naše prostředí a stránky na GitHub." + diplomat_join_pref_github: "Najděte soubor pro svůj jazyk " + diplomat_github_url: "na GitHub" + diplomat_join_suf_github: ", upravte ho online a zašlete pull request. Také zaškrtněte toto políčko, abyste zůstali informováni o novém vývoji internacionalizace!" diplomat_subscribe_desc: "Dostávat emailem oznámení a informace o internacionalizaci a o úrovních k překladu." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." ambassador_introduction: "Zde se tvoří komunita a vy jste její spojení. Využíváme chat, emaily a sociální sítě se spoustou lidí k informování a diskuzím a seznámení s naší hrou. Chcete-li pomoci lidem se přidat a pobavit se a získat dobrý náhled na CodeCombat a kam směřujeme, pak toto je vaše role." ambassador_attribute_1: "Komunikační schopnosti. Schopnost identifikovat problémy hráčů a pomoci jim je řešit. Naslouchat co hráči říkají, co mají rádi a co rádi nemají a komunikovat to zpět ostatním!" ambassador_join_desc: "dejte nám o sobě vědět, o tom co děláte a co byste rádi dělali. Od toho začneme!" ambassador_join_note_strong: "Poznámka" ambassador_join_note_desc: "Jedna z našich priorit je vytvoření vícehráčové hry, kde hráč, který má problém s řešením úrovní může oslovit a požádat o pomoc zkušenější kouzelníky. To je přesně ten případ a místo pro pomoc Velvyslance . Dáme vám vědět více!" - more_about_ambassador: "Dozvědět se více o tom, jak se stát nápomocným Velvyslancem" ambassador_subscribe_desc: "Dostávat emailem oznámení a informace o vývoji v podpoře a vícehráčové hře." changes_auto_save: "Změny jsou automaticky uloženy při kliknutí na zaškrtávací políčka." diligent_scribes: "Naši pilní Písaři:" @@ -695,177 +999,237 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr translating_diplomats: "Naši překladatelští Diplomati:" helpful_ambassadors: "Naši nápomocní Velvyslanci:" -# ladder: -# please_login: "Please log in first before playing a ladder game." -# my_matches: "My Matches" -# simulate: "Simulate" -# simulation_explanation: "By simulating games you can get your game ranked faster!" -# simulate_games: "Simulate Games!" -# simulate_all: "RESET AND SIMULATE GAMES" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" -# battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " -# summary_wins: " Wins, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" -# rank_submitting: "Submitting..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" -# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." -# choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" -# tutorial_play: "Play Tutorial" -# tutorial_recommended: "Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" -# tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" -# warmup: "Warmup" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + ladder: + please_login: "Před hraním hry o umístění se nejprve přihlašte prosím." + my_matches: "Mé souboje" + simulate: "Simulovat" + simulation_explanation: "Simulováním hry odehrajete hru rychleji!" + simulate_games: "Simulovat hry!" + simulate_all: "RESETOVAT A SIMULOVAT HRY" + games_simulated_by: "Hry simulovány vámi:" + games_simulated_for: "Hry simulovány pro vás:" + games_simulated: "Her simulováno" + games_played: "Her hráno" + ratio: "Poměr" + leaderboard: "Žebříček" + battle_as: "Bojovat jako " + summary_your: "Vašich " + summary_matches: "Soubojů - " + summary_wins: " Vyhráno, " + summary_losses: " Prohráno" + rank_no_code: "Žádný nový kód pro ohodnocení" + rank_my_game: "Ohodnotit mou hru!" + rank_submitting: "Odesílání..." + rank_submitted: "Odesláno pro ohodnocení" + rank_failed: "Chyba při ohodnocení" + rank_being_ranked: "Hra je ohodnocována" + rank_last_submitted: "odesláno " + help_simulate: "Pomoc simulovat hry?" + code_being_simulated: "Váš nový kód je právě simulován jiným hráčem pro ohodnocení." + no_ranked_matches_pre: "Žádné ohodnocené souboje pro " + no_ranked_matches_post: " tým! Hraj proti soupeřům a poté přijď zpět pro ohodnocení své hry." + choose_opponent: "Vybrat soupeře" + select_your_language: "Vyber svůj jazyk!" + tutorial_play: "Hrát Tutoriál" + tutorial_recommended: "Doporučeno, pokud jste ještě nikdy nehráli" + tutorial_skip: "Přeskočit Tutoriál" + tutorial_not_sure: "Nejste si jisti, co se děje?" + tutorial_play_first: "Hrát nejprve Tutoriál." + simple_ai: "Jednoduchá obtížnost" + warmup: "Rozehřátí" + friends_playing: "Přátel hraje" + log_in_for_friends: "Pro hraní s přáteli se nejprve přihlašte!" + social_connect_blurb: "Připojte se a hrajte proti svým přátelům!" + invite_friends_to_battle: "Pozvěte své přátele, abyste spolu bojovali!" + fight: "Boj!" + watch_victory: "Sledovat vaši výhru" + defeat_the: "Poraženo" +# tournament_started: ", started" + tournament_ends: "Turnaj končí" + tournament_ended: "Turnaj ukončen" + tournament_rules: "Pravidla turnaje" + tournament_blurb: "Pište kód, sbírejte zlato, budujte armádu, zabijte nepřátele, vyhrajte ceny a vylepšte si svou kariéru v našem turnaji o $40,000 dolarů! Podívejte se na detaily" + tournament_blurb_criss_cross: "Vyhrajte nabídky, budujte cesty, přelstěte nepřátele, seberte drahokamy a velepšte si svou kariéru v našem Křížovkářském turnaji! Podívejte se na detaily" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" + tournament_blurb_blog: "na našem blogu" + rules: "Pravidla" + winners: "Vítězové" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + user: + stats: "Statistiky" + singleplayer_title: "Singleplayer Úrovně" + multiplayer_title: "Multiplayer Úrovně" + achievements_title: "Úspěchy" + last_played: "Poslední hraná" + status: "Stav" + status_completed: "Dokončeno" + status_unfinished: "Nedokončeno" + no_singleplayer: "Zatím žádné Singleplayer hry nebyly hrány." + no_multiplayer: "Zatím žádné Multiplayer hry nebyly hrány." + no_achievements: "Zatím žádné Úspěchy." + favorite_prefix: "Oblíbený jazky: " + favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + achievements: + last_earned: "Poslední obdržený" + amount_achieved: "Množství" + achievement: "Úspěch" + category_contributor: "Přispěvatel" + category_ladder: "Žebříček" + category_level: "Úroveň" + category_miscellaneous: "Ostatní" + category_levels: "Úrovně" + category_undefined: "Nezařazeno" + current_xp_prefix: "" + current_xp_postfix: " celkem" + new_xp_prefix: "" + new_xp_postfix: " získáno" + left_xp_prefix: "" + left_xp_infix: " do úrovně " + left_xp_postfix: "" -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." + account: + recently_played: "Nedávno zaplaceno" + no_recent_games: "Žádné hry během posledních dvou týdnů." + payments: "Platby" + purchased: "Zaplaceno" + subscription: "Předplatné" +# invoices: "Invoices" + service_apple: "Apple" + service_web: "Web" + paid_on: "Zaplaceno přes" + service: "Služba" + price: "Cena" + gems: "Drahokamy" + active: "Aktivní" + subscribed: "Odebíráno" + unsubscribed: "Zrušeno odebírání" + active_until: "Aktivní do" + cost: "Náklady" + next_payment: "Další platba" + card: "Karta" + status_unsubscribed_active: "Nejste předplatitel a nebude vám nic zaúčtováno, ale váš účet je pořád aktivní." + status_unsubscribed: "Dostaňte přístup k novým úrovním, hrdinům, předmětům a bonusovým drahokamům s CodeCombat předplatným!" -# loading_error: -# could_not_load: "Error loading from server" -# connection_failure: "Connection failed." -# unauthorized: "You need to be signed in. Do you have cookies disabled?" -# forbidden: "You do not have the permissions." -# not_found: "Not found." -# not_allowed: "Method not allowed." -# timeout: "Server timeout." -# conflict: "Resource conflict." -# bad_input: "Bad input." -# server_error: "Server error." -# unknown: "Unknown error." +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" -# social_network_apis: "Social Network APIs" -# facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" -# facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" -# gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" -# user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" -# patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" -# thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" -# feedback: "Feedback" + loading_error: + could_not_load: "Chyba při načítání ze serveru" + connection_failure: "Chyba připojení." + unauthorized: "Musíte se přihlásit. Máte zapnuté cookies?" + forbidden: "Nemáte oprávnění." + not_found: "Nenalezeno." + not_allowed: "Metoda není povolena." + timeout: "Vypršel časový limit serveru." + conflict: "Konflikt zdrojů." + bad_input: "Špatný vstup." + server_error: "Chyba serveru." + unknown: "Neznámá chyba." -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" + resources: + sessions: "Sezení" + your_sessions: "Vaše sezení" + level: "Úroveň" + social_network_apis: "API sociálních sítí" + facebook_status: "Facebook Stav" + facebook_friends: "Facebook Přátelé" + facebook_friend_sessions: "Sezení Facebook přátel" + gplus_friends: "Google+ Přátelé" + gplus_friend_sessions: "Sezení Google+ přátel" + leaderboard: "Žebříček" + user_schema: "Uživatelské Schéma" + user_profile: "Profil uživatele" + patch: "Oprava" + patches: "Opravy" + patched_model: "Zdrojový dokument" + model: "Model" + system: "Systém" + systems: "Systémy" + component: "Součást" + components: "Součásti" + thang: "Thang" + thangs: "Thangy" + level_session: "Vaše sezení" + opponent_session: "Sezení protivníka" + article: "Článek" + user_names: "Uživatelská jména" + thang_names: "Názvy Thangů" + files: "Soubory" + top_simulators: "Nejlepší simlátory" + source_document: "Zdrojový dokument" + document: "Dokument" + sprite_sheet: "Sprite Sheet" + employers: "Zaměstnavatelé" + candidates: "Kandidáti" + candidate_sessions: "Sezení kandidáta" + user_remark: "Poznámka uživatele" + user_remarks: "Poznámky uživatele" + versions: "Verze" + items: "Předměty" +# hero: "Hero" + heroes: "Hrdinové" + achievement: "Úspěch" + clas: "CLA" + play_counts: "Počet hraní" + feedback: "Zpětná vazba" + payment_info: "Info o platbě" + campaigns: "Kampaně" +# poll: "Poll" +# user_polls_record: "Poll Voting History" -# guide: -# temp: "Temp" +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" + + delta: + added: "Přidáno" + modified: "Změněno" +# not_modified: "Not Modified" + deleted: "Smazáno" + moved_index: "Index přemístěn" + text_diff: "Rozdíl textu" + merge_conflict_with: "SLOUČIT V ROZPORU S" + no_changes: "Žádné změny" + + guide: + temp: "Dočasné" multiplayer: multiplayer_title: "Nastavení Multiplayeru" # We'll be changing this around significantly soon. Until then, it's not important to translate. -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." + multiplayer_toggle: "Zapnout multiplayer" + multiplayer_toggle_description: "Povolit ostatním připojit se do hry." multiplayer_link_description: "Sdílejte tento odkaz s lidmi, kteří se k vám mohou přidat ve hře." multiplayer_hint_label: "Tip:" multiplayer_hint: " Klikněte na odkaz pro jeho výběr, poté stiskněte ⌘-C nebo Ctrl-C pro kopírování odkazu." multiplayer_coming_soon: "Další vlastnosti multiplayeru jsou na cestě!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." + multiplayer_sign_in_leaderboard: "Přihlašte se nebo si vytvořte účet a dostaňte se na žebříček nejlepších." legal: page_title: "Licence" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr practices_title: "Doporučené postupy" practices_description: "Toto je příslib našeho přístupu v jednoduchém jazyce." privacy_title: "Soukromí" - privacy_description: "Neprodáme vaše osobní informace. Náš plán na zhodnocení je založen na poskytování pracovních příležitostí, přesto si můžete být jisti, že vaše osobní informace nebudou distribuovány bez vašeho plného souhlasu." + privacy_description: "Neprodáme žádné vaše osobní informace." security_title: "Zabezpečení" security_description: "Usilujeme o to, abychom udrželi vaše osobní informace v bezpečí. Jako otevřený projekt jsme přístupni komukoliv k provedení kontroly kódu pro zlepšení našich bezpečnostních systémů." email_title: "Email" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr email_description_suffix: "nebo skrze odkazy v odeslaných emailech si můžete nastavit nebo se kdykoliv odhlásit z naší korespondence." cost_title: "Cena" cost_description: "Momentálně je CodeCombat 100% zdarma! Naší snahou je udržet přístup zdarma, abychom dali možnost hrát co největšímu množství lidí. V případě nutnosti budeme muset přejít na placený vstup nebo platbu za přístup k určitému obsahu, ale raději bychom se tomu vyhnuli. S trochou štěstí doufáme v následující plán monetizace:" - recruitment_title: "Nábor" - recruitment_description_prefix: "Zde na CodeCombatu se stanete mocným kouzelníkem a to nejen ve hře, ale i v reálném životě." - url_hire_programmers: "O dobré programátory je stále velký zájem " - recruitment_description_suffix: "takže až se vypracujete a pokud budete souhlasit, budeme demonstrovat vaše nejlepší programátorské úspěchy tisícovkám zaměstnavatelů, kteří by vás rádi zaměstnali. Ti nám zaplatí něco málo, ale vám pak zaplatí " - recruitment_description_italic: "daleko více," - recruitment_description_ending: "tato hra zůstane zdarma a všichni budou spokojeni. Takový je plán." copyrights_title: "Copyrights a Licence" contributor_title: "Licenční ujednání přispívatelů (CLA)" contributor_description_prefix: "Všichni přispívatelé jak na webu tak do projektu na GitHubu spadají pod naše " @@ -902,7 +1260,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr code_description_prefix: "Veškerý kód vlastněný CodeCombatem nebo hostovaným na codecombat.com, kód v repozitáři na GitHub repository nebo v databázi codecombat.com, je licencován pod " mit_license_url: "MIT licencí" code_description_suffix: "Zahrnut je veškerý kód v systémech a komponentech, které jsou zpřístupněné CodeCombatem pro vytváření úrovní." - art_title: "Art/Hudba - Creative Commons " + art_title: "Obrázky/Hudba - Creative Commons " art_description_prefix: "Veškerý obecný obsah je dostupný pod " cc_license_url: "Mezinárodní Licencí Creative Commons Attribution 4.0" art_description_suffix: "Obecným obsahem se rozumí vše dostupné na CodeCombatu, určené k vytváření úrovní. To zahrnuje:" @@ -928,209 +1286,193 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr nutshell_description: "Vše co je poskytnuto v editoru úrovní je zdarma a mžete toho využít při vytváření úrovní. Ale vyhrazujeme si právo omezit distribuci úrovní samotných (těch, které byly vytvořeny na codecombat.com), takže v budoucnu bude možno tyto zpoplatnit, bude-li to v nejhorším případě nutné" canonical: "Anglická verze tohoto dokumentu je původní, rozhodující verzí. Nastane-li rozdíl v překladu, Anglická verze bude mít vždy přednost." -# ladder_prizes: -# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - - wizard_settings: - title: "Nastavení Kouzelníka" - customize_avatar: "Upravte vás Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" + ladder_prizes: + title: "Ceny v turnaji" # This section was for an old tournament and doesn't need new translations now. + blurb_1: "Tyto ceny budou rozdány v souladu s" + blurb_2: "pravidly turnaje" + blurb_3: "nejlepším lidským a zlobřím hráčům." + blurb_4: "Dva týmy znamenají dvojnásobek cen!" + blurb_5: "(Budou dvě první místa, dvě druhá místa, atd.)" + rank: "Umístění" + prizes: "Ceny" + total_value: "Celková hodnota" + in_cash: "v penězích" + custom_wizard: "Vlastní CodeCombat Kouzelník" + custom_avatar: "Vlastní CodeCombat avatar" + heap: "na šest měsíců \"Startup\" přístupu" + credits: "tvůrci" + one_month_coupon: "kupon: vybrat buď Rails nebo HTML" + one_month_discount: "30% sleva: vybrat buď Rails nebo HTML" + license: "licence" + oreilly: "ebook vlastního výběru" account_profile: -# settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" + settings: "Nastavení" # We are not actively recruiting right now, so there's no need to add new translations for this section. + edit_profile: "Upravit Profil" + done_editing: "Úpravy dokončeny" profile_for_prefix: "Profil pro " -# profile_for_suffix: "" -# featured: "Featured" -# not_featured: "Not Featured" -# looking_for: "Looking for:" -# last_updated: "Last updated:" -# contact: "Contact" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." -# next_name: "name?" -# next_short_description: "write a short description." -# next_long_description: "describe your desired position." -# next_skills: "list at least five skills." -# next_work: "chronicle your work history." -# next_education: "recount your educational ordeals." -# next_projects: "show off up to three projects you've worked on." -# next_links: "add any personal or social links." -# next_photo: "add an optional professional photo." -# next_active: "mark yourself open to offers to show up in searches." -# example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" -# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" -# links_name_help: "What are you linking to?" -# links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" -# basics_city: "City" -# basics_city_help: "City you want to work in (or live in now)." -# basics_country: "Country" -# basics_country_help: "Country you want to work in (or live in now)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" -# basics_looking_for_full_time: "Full-time" -# basics_looking_for_part_time: "Part-time" -# basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." -# work_experience: "Work Experience" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" -# work_duration_help: "When did you hold this gig?" -# work_description: "Description" -# work_description_help: "What did you do there? (140 chars; optional)" -# education: "Education" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." -# education_school: "School" -# education_school_help: "Name of your school." -# education_degree: "Degree" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" -# our_notes: "CodeCombat's Notes" -# remarks: "Remarks" -# projects: "Projects" -# projects_header: "Add 3 projects" -# projects_header_2: "Projects (Top 3)" -# projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" -# project_name_help: "What was the project called?" -# project_description: "Description" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" -# project_link_help: "Link to the project." -# player_code: "Player Code" + profile_for_suffix: "" + featured: "Uvedeno" + not_featured: "Neuvedeno" + looking_for: "Hledám:" + last_updated: "Naposled aktualizováno:" + contact: "Kontakt" + active: "Právě hledá nabídky na pohovor" + inactive: "Právě nehledá nabídky na pohovor" + complete: "dokončeno" + next: "Další" + next_city: "město?" + next_country: "vyberte vaši zemi." + next_name: "jméno?" + next_short_description: "napište krátký popisek." + next_long_description: "popište vaši vysněnou pozici." + next_skills: "vypište alespoň pět dovedností." + next_work: "vypište vaši pracovní historii." + next_education: "vypište vaše tituly." + next_projects: "ukažte nám tři projekty, na kterých jste pracovali." + next_links: "přidejte jakékoliv osobní nebo sociální odkazy." + next_photo: "přidejte dodatečnou profesionální fotku." + next_active: "označte se jako \"Právě hledá nabídky na pohovor\"." + example_blog: "Blog" + example_personal_site: "Osobní stránka" + links_header: "Osobní odkazy" + links_blurb: "Odkažte jakoukoliv jinou stránku nebo profil, kterou chcete vyzdvihnout, jako váš GitHub, váš LinkedIn, nebo váš blog." + links_name: "Název odkazu" + links_name_help: "Kam odkazujete?" + links_link_blurb: "Odkaz" + basics_header: "Update basic info" + basics_active: "Otevřen nabídkám" + basics_active_help: "Chcete nyní nabídku pohovoru?" + basics_job_title: "Název vysněné práce" + basics_job_title_help: "Jakou práci hledáte?" + basics_city: "Město" + basics_city_help: "Město, ve kterém chcete pracovat (nebo ve kterém nyní žijete)." + basics_country: "Země" + basics_country_help: "Země, ve které chcete pracovat (nebo ve které nyní žijete)." + basics_visa: "USA pracovní status" + basics_visa_help: "Jste autorizován pro práci v USA, nebo potřebujete visa sponzorství? (Pokud žijete v Kanadě nebo Austrálii, zaškrtněte Autorizováno.)" + basics_looking_for: "Hledám" + basics_looking_for_full_time: "Plný úvazek" + basics_looking_for_part_time: "Částečný úvazek" + basics_looking_for_remote: "Dálkovou práci" + basics_looking_for_contracting: "Smluvní práci" + basics_looking_for_internship: "Stáž" + basics_looking_for_help: "Jakou chcete pozici vývojáře?" + name_header: "Vyplňte své jméno" + name_anonymous: "Anonymní vývojář" + name_help: "Jméno, pod kterým budete vystupovat, jako \"Jan Novák\"." + short_description_header: "Napište krátký popis sama sebe" + short_description_blurb: "Přidejte slogat, aby se o vás zaměstnavatel dozvěděl více." + short_description: "Slogan" + short_description_help: "Kdo jste, a za čím si jdete? 140 znaků max." + skills_header: "Dovednosti" + skills_help: "Označte relevantní vývojářské dovednosti v pořadí zdatnosti." + long_description_header: "Popište vysněnou pozici" + long_description_blurb: "Řekněte zaměstavatelům jak jste úžasní a jakou roli chcete." + long_description: "Vlastní popis" + long_description_help: "Popište sebe, abyste zaujali zaměstnavatele. Zanechte to krátké a výstižné. Doporučujeme navržení pozice, která vás nejvíce zajímá. 600 znaků max." + work_experience: "Pracovní zkušenosti" + work_header: "Vypište vaši pracovní historii" + work_years: "Let zkušeností" + work_years_help: "Kolik let profesionálních zkušenost (dostávat zaplaceno) vyvýjením softwaru máte?" + work_blurb: "Vypište vaše pracovní zkušenosti, nejnovější jako první." + work_employer: "Zaměstnavatel" + work_employer_help: "Název vašeho zaměstnavatele." + work_role: "Název práce" + work_role_help: "Jak se nazývala vaše práce nebo role?" + work_duration: "Trvání" + work_duration_help: "Jak dlouho jste tam pracoval?" + work_description: "Popisek" + work_description_help: "Co jste tam dělal? (140 znaků; nepovinné)" + education: "Vzdělání" + education_header: "Vypište vaše akademické snažení" + education_blurb: "Vypište své akademické vzdělání." + education_school: "Škola" + education_school_help: "Název vaší školy." + education_degree: "Titul" + education_degree_help: "Jaký je váš titul a obor studia?" + education_duration: "Datumy" + education_duration_help: "Kdy?" + education_description: "Popisek" + education_description_help: "Vyzdvihněte cokoliv o této vzdělávací zkušenosti. (140 znaků; nepovinné)" + our_notes: "Poznámky CodeCombatu" + remarks: "Poznámky" + projects: "Projekty" + projects_header: "Přidejte 3 projekty" + projects_header_2: "Projekty (Nejlepší 3)" + projects_blurb: "Vyzdvihněte vaše projekty k ohromení zaměstnavatelů." + project_name: "Název projektu" + project_name_help: "Jak se projekt jmenoval?" + project_description: "Popisek" + project_description_help: "Krátce popište projekt." + project_picture: "Obrázek" + project_picture_help: "Nahrajte 230x115px nebo větší obrázek ukazující projekt." + project_link: "Odkaz" + project_link_help: "Odkaz na projekt." + player_code: "Kód hráče" -# employers: -# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." -# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." -# hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. -# get_started: "Get Started" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." -# candidate_name: "Name" -# candidate_location: "Location" -# candidate_looking_for: "Looking For" -# candidate_role: "Role" -# candidate_top_skills: "Top Skills" -# candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" + employers: + deprecation_warning_title: "Omlouváme se, CodeCombat právě nepřijímá další žadatele." + deprecation_warning: "Zaměřujeme se na začátečnickou úroveň, místo toho, abychom nalézali vývojářské experty." + hire_developers_not_credentials: "Najímejte vývojáře, ne jen ty s tituly." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. + get_started: "Začnětě" + already_screened: "Již jsme si technicky prověřili všechny naše kandidáty" + filter_further: ", ale můžete si také zapnout filtry:" + filter_visa: "Visa" + filter_visa_yes: "USA autorizováno" + filter_visa_no: "Neautorizováno" + filter_education_top: "Nejlepší škola" + filter_education_other: "Jiné" + filter_role_web_developer: "Webový vývojář" + filter_role_software_developer: "Softwarový vývojářr" + filter_role_mobile_developer: "Mobilní vývojář" + filter_experience: "Zkušený" + filter_experience_senior: "Senior" + filter_experience_junior: "Junior" + filter_experience_recent_grad: "Čerstvý absolvent" + filter_experience_student: "Vysokoškolák" + filter_results: "výsledky" + start_hiring: "Začít přijímat." + reasons: "Tři důvody, pro byste měli přijímat přes nás:" + everyone_looking: "Každý zde hledá svou další příležitost." + everyone_looking_blurb: "Zapomeňte na 20% LinkedIn InMail míru odezvy. Každý, který je uveden na této stránce chce najít svou další pozici a odpoví na váš požadavek na představení se." + weeding: "Opřete se; odstranili jsme všechny mouchy za vás." + weeding_blurb: "Každý hráč, který je uveden byl prověřen o svých technických dovednostech. Kandidáti také uvádí poznámky na svých profilech, aby vám ušetřili čas." + pass_screen: "Projdou vašimi technickými prověrkami." + pass_screen_blurb: "Prohlédněte si kód každého kandidáta. Takto zjistíte, jestli se vám kandidát bude hodit či nikoliv." + make_hiring_easier: "Ulehčete mi přijímání zaměstananců, prosím." + what: "Co je CodeCombat?" + what_blurb: "CodeCombat je multiplayerová prohlížečová programovací hra. Hráči píší köd, kterým ovládají jejich jednotky v boji proti jiným vývojářům. Naši hráči mají dobré zkušenosti s technikou." + cost: "Kolik si účtujeme?" + cost_blurb: "Účtujeme si 15% z platu po dobu prvního roku a nabízíme 100% záruku vrácení peněz po dobu 90 dní. Neúčtujeme si za kandidáty, se kterými se již aktivně vede pohovor ve vaší společnosti." + candidate_name: "Jméno" + candidate_location: "Umístění" + candidate_looking_for: "Hledá" + candidate_role: "Role" + candidate_top_skills: "Nejlepší dovednosti" + candidate_years_experience: "Roků zkušeností" + candidate_last_updated: "Naposled aktualizováno" + candidate_who: "Kdo" + featured_developers: "Uvedení vývojáři" + other_developers: "Jiní vývojáři" + inactive_developers: "Neaktivní vývojáři" admin: -# av_espionage: "Espionage" # Really not important to translate /admin controls. -# av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" -# av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" + av_espionage: "Špionáž" # Really not important to translate /admin controls. + av_espionage_placeholder: "Email nebo jméno" + av_usersearch: "Hledat uživatele" + av_usersearch_placeholder: "Email, jméno, příjmení, cokoliv" + av_usersearch_search: "Hledat" av_title: "Administrátorský pohled" av_entities_sub_title: "Entity" av_entities_users_url: "Uživatelé" av_entities_active_instances_url: "Aktivní instance" -# av_entities_employer_list_url: "Employer List" -# av_entities_candidates_list_url: "Candidate List" -# av_entities_user_code_problems_list_url: "User Code Problems List" + av_entities_employer_list_url: "Seznam zaměstnavatelů" + av_entities_candidates_list_url: "Seznam kandidátů" + av_entities_user_code_problems_list_url: "Seznam problémů uživatelských kódů" av_other_sub_title: "Ostatní" av_other_debug_base_url: "Base (pro debugování base.jade)" u_title: "Seznam uživatelů" -# ucp_title: "User Code Problems" + ucp_title: "Problémy uživatelských kódů" lg_title: "Poslední hry" -# clas: "CLAs" + clas: "CLAs" diff --git a/app/locale/da.coffee b/app/locale/da.coffee index 9632260a0..142bbc3bd 100644 --- a/app/locale/da.coffee +++ b/app/locale/da.coffee @@ -3,25 +3,26 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans slogan: "Lær at Kode ved at Spille et Spil" no_ie: "CodeCombat kan desværre ikke køre i Internet Explorer 8 eller ældre. Beklager!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat er ikke designet til mobile enheder og vil måske ikke virke!" # Warning that shows up on mobile devices - play: "Spil" # The big play button that just starts playing a level + play: "Spil" # The big play button that opens up the campaign view. old_browser: "Åh åh, din browser er for gammel til at køre CodeCombat. Beklager!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Du kan godt prøve alligevel, men det vil nok ikke virke." + ipad_browser: "Dårlige nyheder: CodeCombat virker ikke i browseren på iPad. Gode nyheder: vores iPad app afventer kun Apple's godkendelse." campaign: "Kampagne" for_beginners: "For Begyndere" multiplayer: "Multiplayer" # Not currently shown on home page for_developers: "For Udviklere" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Eller download til iPad" nav: play: "Spil" # The top nav bar entry where players choose which levels to play -# community: "Community" + community: "Fællesskab" editor: "Editor" blog: "Blog" forum: "Forum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + account: "Konto" + profile: "Profil" + stats: "Statistik" + code: "Kode" admin: "Admin" # Only shows up when you are an admin home: "Hjem" contribute: "Bidrag" @@ -29,7 +30,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans about: "Om" contact: "Kontakt" twitter_follow: "Følg" -# teachers: "Teachers" + teachers: "Lærere" modal: close: "Luk" @@ -49,233 +50,262 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans play: play_as: "Spil Som " # Ladder page spectate: "Observér" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + players: "spillere" # Hover over a level on /play + hours_played: "timer spillet" # Hover over a level on /play + items: "Udstyr" # Tooltip on item shop button from /play + unlock: "Lås op" # For purchasing items and heroes + confirm: "Bekræft" + owned: "Ejer" # For items you own + locked: "Låst" + purchasable: "Købbar" # For a hero you unlocked but haven't purchased + available: "Tilgængelig" + skills_granted: "Opnåede evner" # Property documentation details + heroes: "Helte" # Tooltip on hero shop button from /play + achievements: "Bedrifter" # Tooltip on achievement list button from /play + account: "Konto" # Tooltip on account button from /play + settings: "Indstillinger" # Tooltip on settings button from /play + poll: "Meningsmåling" # Tooltip on poll button from /play + next: "Næste" # Go from choose hero to choose inventory before playing a level + change_hero: "Skift helt" # Go back from choose inventory to choose hero + choose_inventory: "Vælg udstyr" + buy_gems: "Køb diamanter" + subscription_required: "Kræver abonnement" + anonymous: "Anonym spiller" level_difficulty: "Sværhedsgrad: " campaign_beginner: "Begynderkampagne" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Vælg Dit Level" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Du kan hoppe til et hvilket som helst level herunder, eller diskutere levels på " - adventurer_forum: "Eventyrer-forummet" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... hvor du lærer programmeringens kunst." - campaign_dev: "Tilfældige Sværere Niveauer" - campaign_dev_description: "... hvor du lærer grænsefladen imens du udfører lidt sværere opgaver." + awaiting_levels_adventurer_prefix: "Vi frigiver nye baner hver uge." + awaiting_levels_adventurer: "Skriv dig op som eventyrer" + awaiting_levels_adventurer_suffix: "for at blive den første til at spille nye baner." + adjust_volume: "Indstil lydstyrke" campaign_multiplayer: "Multiplayer Arenaer" campaign_multiplayer_description: "... hvor du koder ansigt-til-ansigt imod andre spillere." - campaign_player_created: "Spillerkreerede" - campaign_player_created_description: "... hvor du kæmper mod dine med-<a href=\"/contribute#artisan\">Kunsthåndværker-troldmænd</a>s kreativitet." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Du gør godt fremskridt! Fortæl din forælder hvor meget du har lært med CodeCombat." + email_invalid: "Ugyldig emailaddresse." + form_blurb: "Indtast din forælders email nedenfor, så viser vi dem det!" + form_label: "Email" + placeholder: "emailaddresse" + title: "Fremragende arbejde, Lærling" login: sign_up: "opret ny konto" log_in: "Log Ind" -# logging_in: "Logging In" + logging_in: "Logger ind" log_out: "Log Ud" - recover: "genskab konto" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Glemt dit kodeord?" + authenticate_gplus: "Forbind med G+" + load_profile: "Load G+ Profil" + finishing: "Færdiggører" + sign_in_with_facebook: "Log ind med Facebook" + sign_in_with_gplus: "Sign in with G+" + signup_switch: "Vil du oprette en konto?" signup: - create_account_title: "Opret en konto for at gemme dit fremskridt" - description: "Det er gratis. Du skal bare indtaste et par ting, så er du klar til at komme igang:" email_announcements: "Modtag nyheder på email" - coppa: "13+ eller ikke-USA" - coppa_why: "(Hvorfor?)" creating: "Opretter Konto..." sign_up: "Registrer" log_in: "Log ind med password" -# social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." + social_signup: "Eller du kan skrive dig op med Facebook eller G+:" + required: "Du skal logge ind før du kan gå den vej." + login_switch: "Har du allerede en konto?" recover: recover_account_title: "genskab konto" send_password: "Send kodeord" -# recovery_sent: "Recovery email sent." + recovery_sent: "Gendannelsesemail sendt." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Primære" + secondary: "Sekundære" + armor: "Rustning" + accessories: "Ekstraudstyr" + misc: "Diverse" + books: "Bøger" common: + back: "Gå tilbage" # When used as an action verb, like "Navigate backward" + continue: "Fortsæt" # When used as an action verb, like "Continue forward" loading: "Henter..." saving: "Gemmer..." sending: "Sender..." -# send: "Send" + send: "Send" cancel: "Annuller" save: "Gem" -# publish: "Publish" -# create: "Create" + publish: "Offentliggør" + create: "Skab" manual: "Manual" fork: "Forgren" play: "Spil" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + retry: "Prøv igen" + actions: "Handlinger" + info: "Info" + help: "Hjælp" + watch: "Hold øje" + unwatch: "Stop med at holde øje" + submit_patch: "Indsend patch" + submit_changes: "Indsend ændringer" + save_changes: "Gem ændringer" general: and: "og" name: "navn" -# date: "Date" + date: "Dato" body: "krop" version: "version" + pending: "Afventer" + accepted: "Accepteret" + rejected: "Afvist" + withdrawn: "Trukket tilbage" + submitter: "Indsender" + submitted: "Indsendt" commit_msg: "ændringsnotat" -# version_history: "Version History" + review: "Gennemsyn" + version_history: "Versionshistorie" version_history_for: "versionhistorie for: " + select_changes: "Vælg to ændringer nedenfor for at se forskellen." + undo_prefix: "Fortryd" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Gentag" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Afspil forhåndsvisning af nuværende bane" result: "Resultat" results: "resultater" description: "beskrivelse" or: "eller" -# subject: "Subject" - email: "e-mail" -# password: "Password" + subject: "Emne" + email: "email" + password: "Kodeord" message: "Besked" -# code: "Code" -# ladder: "Ladder" + code: "Kode" + ladder: "Rangstige" when: "Når" -# opponent: "Opponent" + opponent: "Modstander" rank: "Rang" -# score: "Score" + score: "Score" win: "Sejr" loss: "Tab" tie: "Uafgjort" - easy: "Nem" -# medium: "Medium" + easy: "Let" + medium: "Mellem" hard: "Svær" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player: "Spiller" + player_level: "Niveau" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Krigsherre" +# ranger: "Ranger" + wizard: "Troldmand" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "sekund" + seconds: "sekunder" + minute: "minut" + minutes: "minutter" + hour: "time" + hours: "timer" + day: "dag" + days: "dage" + week: "uge" + weeks: "uger" + month: "måned" + months: "måneder" + year: "år" + years: "år" play_level: done: "Færdig" home: "Hjem" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "Bane" # Like "Level: Dungeons of Kithgard" + skip: "Spring over" + game_menu: "Spilmenu" guide: "Guide" restart: "Start forfra" goals: "Mål" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" + goal: "Mål" + running: "Kører..." + success: "Success!" + incomplete: "Ufærdig" + timed_out: "Løb tør for tid" + failing: "Fejler" action_timeline: "Handlingstidslinje" click_to_select: "Klik på en enhed for at vælge" -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "Flerspiller" + control_bar_join_game: "Tilslut spil" + reload: "Genindlæs" reload_title: "Genindlæs alt kode?" reload_really: "Er du sikker på at du ønsker at genindlæse denne bane helt fra begyndelsen?" reload_confirm: "Genindlæs alt" + victory: "Sejr" # victory_title_prefix: "" victory_title_suffix: " Færdig" victory_sign_up: "Opret dig for at gemme dit fremskridt" victory_sign_up_poke: "Ønsker du at gemme din kode? Opret en gratis konto!" victory_rate_the_level: "Bedøm denne bane: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Spil næste bane" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_return_to_ladder: "Returner til rangstige" + victory_play_continue: "Fortsæt" + victory_saving_progress: "Gemmer fremskridt" victory_go_home: "Gå hjem" # Only in old-style levels. victory_review: "Fortæl os mere!" # Only in old-style levels. victory_hour_of_code_done: "Er du færdig?" victory_hour_of_code_done_yes: "Ja, jeg er færdig med min Kodetime!" + victory_experience_gained: "XP tjent" + victory_gems_gained: "Diamanter tjent" + victory_new_item: "Nyt udstyr" + victory_viking_code_school: "For dælen det var en svær bane du lige slog! Hvis ikke du allerede er softwareudvikler, så burde du blive det. Du er lige kommet foran i køen til at blive accepteret hos Viking Code School, du kan tage dine evner til det næste niveau og blive en professionel webudvikler på 14 uger." + victory_become_a_viking: "Bliv en Viking" guide_title: "Instruktioner" -# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. -# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. + tome_minion_spells: "Dine Minions' besværgelser" # Only in old-style levels. + tome_read_only_spells: "Læsebesværgelser" # Only in old-style levels. tome_other_units: "Andre enheder" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_run: "Kør" + tome_cast_button_running: "Kører" + tome_cast_button_ran: "Kørt" + tome_submit_button: "Indsend" + tome_reload_method: "Genindlæs den originale kode til denne metode" # Title text for individual method reload button. + tome_select_method: "Vælg en metode" + tome_see_all_methods: "Se alle metoder du kan redigere" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Vælg nogen til at " tome_available_spells: "Tilgængelige trylleformularer" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_your_skills: "Dine evner" + tome_help: "Hjælp" + tome_current_method: "Nuværende metode" + hud_continue_short: "Fortsæt" + code_saved: "Kode gemt" skip_tutorial: "Spring over (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." + keyboard_shortcuts: "Tastaturgenveje" + loading_ready: "Klar!" + loading_start: "Start bane" + problem_alert_title: "Ret din kode" + problem_alert_help: "Hjælp" + time_current: "Nu:" + time_total: "Max:" + time_goto: "Gå til:" + non_user_code_problem_title: "Kan ikke indlæse banen" + infinite_loop_title: "Uendelig løkke detekteret" + infinite_loop_description: "Den indledende kode til at bygge verdenen blev aldrig færdig med at køre. Den er sandsynligvis enten meget langsom eller har en uendeligt løkke. Eller også er der en bug. Du kan enten prøve at køre denne kode igen eller nulstille koden til den oprindelige tilstand. Hvis ikke det virker må du meget gerne fortælle os det." + check_dev_console: "Du kan også åbne udviklerkonsollen for at se hvad der kunne være galt." + check_dev_console_link: "(vejledning)" + infinite_loop_try_again: "Prøv igen" + infinite_loop_reset_level: "Nulstil bane" + infinite_loop_comment_out: "Udkommenter min kode" + tip_toggle_play: "Skift mellem afspil/pause med Ctrl+P." + tip_scrub_shortcut: "Brug Ctrl+[ og Ctrl+] til at spole tilbage og frem." + tip_guide_exists: "Klik på guiden i spilmenuen (i toppen af siden) for brugbar info." + tip_open_source: "CodeCombat er 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat søsatte sin beta i oktober, 2013." + tip_think_solution: "Tænk på løsningen, ikke problemet." + tip_theory_practice: "I teorien er der ingen forskel på teori og praksis. Men i praksis er der. - Yogi Bjørn" + tip_error_free: "Der findes to måder at skrive fejlfrie programmer; kun den tredje virker. - Alan Perlis" + tip_debugging_program: "Hvis debugging er at fjerne kodefejl, så må programmering være at proppe fejl ind i koden. - Edsger W. Dijkstra" + tip_forums: "Kig over i vores forum og fortæl os hvad du synes!" + tip_baby_coders: "I fremtiden vil selv babier være Ærketroldmænd." + tip_morale_improves: "Indlæsning vil fortsætte indtil moralen forbedres." + tip_all_species: "Vi tror på lige muligheder for at lære programmering for alle arter." # tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." + tip_harry: "Du' en troldmand, " + tip_great_responsibility: "Med store kodeevner kommer stort fejlfindingsansvnar." # tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." # tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." # tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Tilpas troldmand" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" multiplayer_tab: "Flere spillere" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Gem ny version" new_major_version: "Ny hoved Version" +# submitting_patch: "Submitting Patch..." cla_prefix: "For at gemme dine ændringer, må du acceptere brugerbetingelserne" cla_url: "CLA" cla_suffix: "." cla_agree: "Jeg er enig" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Kontakt CodeCombat" welcome: "Godt at høre fra dig! Brug denne formular til at sende os en email. " - contribute_prefix: "Hvis du er interesseret i at bidrage, så tjek vores " - contribute_page: "bidragsside" - contribute_suffix: "!" forum_prefix: "For noget offentligt, prøv venligst " forum_page: "vores forum" forum_suffix: " istedet." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans autosave: "Ændringer Gemmes Automatisk" me_tab: "Mig" picture_tab: "Billede" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Password" emails_tab: "Emails" # admin: "Admin" new_password: "Nyt Password" new_password_verify: "Bekræft" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "Emailtilmeldinger" # email_subscriptions_none: "No Email Subscriptions." email_announcements: "Nyheder" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" - wizard_tab: "Troldmand" - wizard_color: "Farve på Troldmandstøj" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "Ærkemager" archmage_title_description: "(Programmør)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "Artisan" artisan_title_description: "(Banedesigner)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "Eventyrer" adventurer_title_description: "(Banetester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Skriver" scribe_title_description: "(Artikkel redaktør)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "Diplomat" diplomat_title_description: "(Oversætter)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "Ambassadør" ambassador_title_description: "(Brugerstøtte)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # thang_title: "Thang Editor" level_title: "Bane Redigeringsværktøj" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # level_tab_thangs_all: "All" level_tab_thangs_conditions: "Startbetingelser" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" level_settings_title: "Instillinger" level_component_tab_title: "Nuværende komponenter" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" article_search_title: "Søg Artikler Her" # thang_search_title: "Search Thang Types Here" level_search_title: "Søg Baner Her" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Forhåndsvisning" edit_article_title: "Ændr artikkel" +# polls: +# priority: "Priority" + contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." - introduction_desc_ending: "Vi håber du vil deltage i vores fest!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy, ogMatt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" alert_account_message_intro: "Hej med dig!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # join_desc_4: "and we'll go from there!" join_url_email: "Skriv til os" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans artisan_join_step2: "Lav en ny bane og udforsk eksisterende baner." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Troldmandsinstillinger" - customize_avatar: "Tilpas din avatar" -# active: "Active" -# color: "Color" -# group: "Group" - clothes: "Påklædning" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/de-AT.coffee b/app/locale/de-AT.coffee index cca647469..05e1ad308 100644 --- a/app/locale/de-AT.coffee +++ b/app/locale/de-AT.coffee @@ -3,14 +3,15 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: slogan: "Lerne spielend Programmieren" no_ie: "CodeCombat läuft nicht im IE8 oder älteren Browsern. Tut uns Leid!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat ist nicht für Mobilgeräte optimiert und funktioniert möglicherweise nicht." # Warning that shows up on mobile devices - play: "Spielen" # The big play button that just starts playing a level + play: "Spielen" # The big play button that opens up the campaign view. old_browser: "Oh! Dein Browser ist zu alt für CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Du kannst es trotzdem versuchen, aber es wird wahrscheinlich nicht funktionieren." + ipad_browser: "Schlechte Nachricht: CodeCombat funktioniert im iPad-Browser nicht. Gute Nachricht: Unsere iPad App wartet auf das OK von Apple." campaign: "Kampagne" for_beginners: "Für Anfänger" multiplayer: "Mehrspieler" # Not currently shown on home page for_developers: "Für Entwickler" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Oder für iPad runterladen" nav: play: "Spielen" # The top nav bar entry where players choose which levels to play @@ -29,7 +30,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: about: "Über" contact: "Kontakt" twitter_follow: "Twitter" -# teachers: "Teachers" + teachers: "Lehrer" modal: close: "Schließen" @@ -53,65 +54,63 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: hours_played: "Stunden gespielt" # Hover over a level on /play items: "Gegenstände" # Tooltip on item shop button from /play # unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" + confirm: "Bestätigen" # owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" + locked: "Gesperrt" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased + available: "Verfügbar" # skills_granted: "Skills Granted" # Property documentation details heroes: "Helden" # Tooltip on hero shop button from /play - achievements: "Achievements" # Tooltip on achievement list button from /play + achievements: "Erfolge" # Tooltip on achievement list button from /play account: "Account" # Tooltip on account button from /play settings: "Einstellungen" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero +# poll: "Poll" # Tooltip on poll button from /play + next: "Weiter" # Go from choose hero to choose inventory before playing a level + change_hero: "Held wechseln" # Go back from choose inventory to choose hero choose_inventory: "Gegenstände ausrüsten" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + buy_gems: "Juwelen kaufen" +# subscription_required: "Subscription Required" + anonymous: "Anonymer Spieler" level_difficulty: "Schwierigkeit: " campaign_beginner: "Anfängerkampagne" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Wähle dein Level" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Du kannst zu jedem Level springen oder diskutiere die Level " - adventurer_forum: "im Abenteurerforum" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... in der Du die Zauberei der Programmierung lernst." - campaign_dev: "Beliebiges schwierigeres Level" - campaign_dev_description: "... in welchem Du die Bedienung erlernst, indem Du etwas schwierigeres machst." +# adjust_volume: "Adjust volume" campaign_multiplayer: "Multiplayerarena" campaign_multiplayer_description: "... in der Du Kopf-an-Kopf gegen andere Spieler programmierst." - campaign_player_created: "Von Spielern erstellt" - campaign_player_created_description: "... in welchem Du gegen die Kreativität eines <a href=\"/contribute#artisan\">Artisan Zauberers</a> kämpfst." - campaign_classic_algorithms: "Klassiche Algorithmen" - campaign_classic_algorithms_description: "... in welchem du die populärsten Algorithmen der Informatik lernst." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "Registrieren" log_in: "Einloggen" logging_in: "Logge ein" log_out: "Ausloggen" - recover: "Account wiederherstellen" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Passwort vergessen?" + authenticate_gplus: "G+ authentifizieren" + load_profile: "G+ Profil laden" + finishing: "Fertig stellen" + sign_in_with_facebook: "Mit Facebook anmelden" + sign_in_with_gplus: "Mit G+ anmelden" + signup_switch: "Möchtest du einen Account erstellen?" signup: - create_account_title: "Account anlegen, um Fortschritt zu speichern" - description: "Es ist kostenlos. Nur noch ein paar Dinge, dann kannst Du loslegen." email_announcements: "Erhalte Benachrichtigungen per Email" - coppa: "Älter als 13 oder nicht aus den USA" - coppa_why: "(Warum?)" creating: "Erzeuge Account..." sign_up: "Neuen Account anlegen" log_in: "mit Passwort einloggen" social_signup: "oder, du registriest dich über Facebook oder G+:" required: "Du musst dich vorher einloggen um dort hin zu gehen." + login_switch: "Du hast bereits einen Account?" recover: recover_account_title: "Account Wiederherstellung" @@ -119,14 +118,16 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: recovery_sent: "Wiederherstellungs-Email versandt." items: -# primary: "Primary" -# secondary: "Secondary" + primary: "Primär" + secondary: "Sekundär" armor: "Rüstung" accessories: "Zubehör" misc: "Sonstiges" -# books: "Books" + books: "Bücher" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Lade..." saving: "Speichere..." sending: "Übertrage..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: fork: "Fork" play: "Abspielen" # When used as an action verb, like "Play next level" retry: "Erneut versuchen" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" submit_patch: "Patch einreichen" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" general: and: "und" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: date: "Datum" body: "Inhalt" version: "Version" - commit_msg: "Commit Nachricht" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" + submitter: "Veröffentlicher" + submitted: "Veröffentlicht" + commit_msg: "Nachricht absenden" +# review: "Review" version_history: "Versionshistorie" version_history_for: "Versionsgeschichte für: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Vorschau vom aktuellen Level spielen" result: "Ergebnis" results: "Ergebnisse" description: "Beschreibung" @@ -173,7 +192,10 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: medium: "Mittel" hard: "Schwer" player: "Spieler" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Spielerlevel" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" units: second: "Sekunde" @@ -194,44 +216,44 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: play_level: done: "Fertig" home: "Startseite" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" + level: "Mission" # Like "Level: Dungeons of Kithgard" + skip: "Überspringen" game_menu: "Spielmenü" guide: "Hilfe" restart: "Neustart" goals: "Ziele" -# goal: "Goal" + goal: "Ziel" # running: "Running..." success: "Erfolgreich!" incomplete: "Unvollständig" timed_out: "Zeit abgelaufen" # failing: "Failing" - action_timeline: "Aktionszeitstrahl" + action_timeline: "Aktionszeitlinie" click_to_select: "Klicke auf eine Einheit, um sie auszuwählen." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "Mehrspieler" + control_bar_join_game: "Spiel beitreten" + reload: "Neu laden" reload_title: "Gesamten Code neu laden?" reload_really: "Bist Du sicher, dass Du das Level neu beginnen willst?" reload_confirm: "Alles neu laden" + victory: "Sieg" victory_title_prefix: "" victory_title_suffix: " Abgeschlossen" victory_sign_up: "Melde Dich an, um Fortschritte zu speichern" victory_sign_up_poke: "Möchtest Du Neuigkeiten per Mail erhalten? Erstelle einen kostenlosen Account und wir halten Dich auf dem Laufenden." victory_rate_the_level: "Bewerte das Level: " # Only in old-style levels. victory_return_to_ladder: "Zurück zur Rangliste" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Spiel das nächste Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "Weiter" + victory_saving_progress: "Speichere Fortschritt" victory_go_home: "Geh auf die Startseite" # Only in old-style levels. victory_review: "Erzähl uns davon!" # Only in old-style levels. victory_hour_of_code_done: "Bist Du fertig?" victory_hour_of_code_done_yes: "Ja, ich bin mit meiner Code-Stunde fertig!" + victory_experience_gained: "EP erhalten" + victory_gems_gained: "Juwelen erhalten" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Anleitung" tome_minion_spells: "Die Zaubersprüche Deiner Knechte" # Only in old-style levels. tome_read_only_spells: "Nur-lesen Zauberspüche" # Only in old-style levels. @@ -239,31 +261,39 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: # tome_cast_button_run: "Run" # tome_cast_button_running: "Running" # tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_submit_button: "Absenden" + tome_reload_method: "Lade den ursprünglichen Code dieser Methode" # Title text for individual method reload button. + tome_select_method: "Wähle eine Methode" + tome_see_all_methods: "Zeige alle editierbaren Methoden" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Wähle jemanden aus, um " tome_available_spells: "Verfügbare Zauber" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_your_skills: "Deine Fähigkeiten" + tome_help: "Hilfe" + tome_current_method: "Aktuelle Methode" + hud_continue_short: "Weiter" + code_saved: "Code gespeichert" skip_tutorial: "Überspringen (Esc)" keyboard_shortcuts: "Tastenkürzel" loading_ready: "Bereit!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" + loading_start: "Starte Level" + problem_alert_title: "Korrigiere deinen Code" + problem_alert_help: "Hilfe" time_current: "Aktuell" time_total: "Total" time_goto: "Gehe zu" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Erneut versuchen" infinite_loop_reset_level: "Level zurücksetzen" infinite_loop_comment_out: "Meinen Code auskommentieren" tip_toggle_play: "Wechsel zwischen Play und Pause mit Strg+P." - tip_scrub_shortcut: "Spule vor und zurück mit Strg+[ und Strg+]" + tip_scrub_shortcut: "Spule vor und zurück mit Strg+[ und Strg+]" # {change} tip_guide_exists: "Klicke auf die Anleitung am oberen Ende der Seite für nützliche Informationen" tip_open_source: "CodeCombat ist 100% quelloffen!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat startete seine Beta im Oktober 2013." tip_think_solution: "Denke über die Lösung nach, nicht über das Problem." tip_theory_practice: "In der Theorie gibt es keinen Unterschied zwischen Theorie und Praxis. In der Praxis schon. - Yogi Berra" @@ -288,68 +318,190 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: tip_hardware_problem: "Q: Wie viele Programmierer braucht man um eine Glühbirne auszuwechseln? A: Keine, es ist ein Hardware-Problem." # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Bearbeite den Zauberer" + tip_brute_force: "Im Zweifelsfall, verwende rohe Gewalt. - Ken Thompson" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventar" save_load_tab: "Speichere/Lade" options_tab: "Einstellungen" guide_tab: "Guide" + guide_video_tutorial: "Video Tutorial" + guide_tips: "Tipps" multiplayer_tab: "Mehrspieler" -# auth_tab: "Sign Up" + auth_tab: "Anmelden" inventory_caption: "Rüste deinen Helden aus" choose_hero_caption: "Wähle Helden, Sprache" save_load_caption: "... und schaue dir die Historie an" options_caption: "konfiguriere Einstellungen" guide_caption: "Doku und Tipps" multiplayer_caption: "Spiele mit Freunden!" -# auth_caption: "Save your progress." + auth_caption: "Fortschritt speichern." + +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" inventory: choose_inventory: "Gegenstände ausrüsten" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + equipped_item: "Ausgerüstet" +# required_purchase_title: "Required" + available_item: "Verfügbar" + restricted_title: "Eingeschränkt" + should_equip: "(Doppelklick um auszurüsten)" + equipped: "(Ausgerüstet)" + locked: "(Gesperrt)" + restricted: "(Eingeschränkt in diesem Level)" + equip: "Ausrüsten" + unequip: "Ablegen" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + buy_gems: + few_gems: "Ein paar Juwelen" + pile_gems: "Ein Haufen Juwelen" + chest_gems: "Truhe voll Juwelen" + purchasing: "Kaufe..." + declined: "Deine Karte wurde abgelehnt" + retrying: "Serverfehler, erneuter Versuch." + prompt_title: "Nicht genug Juwelen" + prompt_body: "Möchtest du mehr?" + prompt_button: "Shop betreten" + recovered: "Voriger Juwelenkauf wiederhergestellt. Bitte die Seite neu laden." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: choose_hero: "Wähle deinen Helden" programming_language: "Programmiersprache" programming_language_description: "Welche Programmiersprache möchtest du verwenden?" -# default: "Default" -# experimental: "Experimental" + default: "Standard" + experimental: "Experimentiell" python_blurb: "Einfach jedoch leistungsfähig, Python ist eine gute Allzweck-Programmiersprache." javascript_blurb: "Die Sprache des Web." coffeescript_blurb: "Schönere JavaScript Syntax." clojure_blurb: "Ein modernes Lisp." - lua_blurb: "Skriptsprache für Spiele." + lua_blurb: "Skriptsprache für Spiele (KI)." io_blurb: "Simpel aber obskur." status: "Status" +# hero_type: "Type" weapons: "Waffen" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" + weapons_warrior: "Schwerter - Kurze Reichweite, keine Magie" + weapons_ranger: "Armbrüste, Pistolen - Hohe Reichweite, keine Magie" + weapons_wizard: "Stöcke, Stäbe - Hohe Reichweite, Magie" + attack: "Angriffsschaden" # Can also translate as "Attack" health: "Gesundheit" speed: "Geschwindigkeit" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + regeneration: "Regeneration" + range: "Reichweite" # As in "attack or visual range" + blocks: "Haltbarkeit" # As in "this shield blocks this much damage" + backstab: "Hinterhaltsschaden" # As in "this dagger does this much backstab damage" + skills: "Fähigkeiten" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." + available_for_purchase: "Zum Kauf verfügbar" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Level zum Freischalten:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Nur bestimmte Helden können dieses Level spielen." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: volume_label: "Lautstärke" music_label: "Musik" music_description: "Schalte Hintergrundmusik an/aus." - autorun_label: "Autorun" - autorun_description: "Steuere automatische Programmausführung." editor_config: "Editor Einstellungen" editor_config_title: "Editor Einstellungen" editor_config_level_language_label: "Sprache für dieses Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: press_paragraph_1_link: "Presse-Paket" press_paragraph_1_suffix: ". Alle Logos und Bilder können ohne unsere vorherige Zustimmung verwendet werden." team: "Team" - george_title: "CEO" + george_title: "CEO" # {change} george_blurb: "Businesser" - scott_title: "Programmierer" + scott_title: "Programmierer" # {change} scott_blurb: "Der Vernünftige" - nick_title: "Programmierer" + nick_title: "Programmierer" # {change} nick_blurb: "Motivationsguru" michael_title: "Programmierer" michael_blurb: "Sys Admin" - matt_title: "Programmierer" + matt_title: "Programmierer" # {change} matt_blurb: "Radfahrer" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Neue Version speichern" new_major_version: "Neue Hauptversion" +# submitting_patch: "Submitting Patch..." cla_prefix: "Damit Änderungen gespeichert werden können, musst du unsere Lizenzbedingungen (" cla_url: "CLA" cla_suffix: ") akzeptieren." cla_agree: "Ich stimme zu" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Kontaktiere CodeCombat" welcome: "Schön von Dir zu hören! Benutze dieses Formular um uns eine Email zu schicken." - contribute_prefix: "Wenn Du Interesse hast, uns zu unterstützen dann sieh dir die " - contribute_page: "Unterstützer Seite" - contribute_suffix: " an!" forum_prefix: "Für alle öffentlichen Themen, benutze stattdessen " forum_page: "unser Forum" forum_suffix: "." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Sende Feedback" contact_candidate: "Kontaktiere Kandidaten" # Deprecated recruitment_reminder: "Benutzen Sie dieses Formular um Kontakt zu Kandidaten aufzunehmen, an denen Sie interessiert sind. Bedenken Sie das CodeCombat 15% des ersten Jahresgehaltes berechnet. Diese Gebühr wird fällig wenn Sie den Kandidaten einstellen und ist für 90 Tage rückerstattungsfähig, sollte der Mitarbeiter nicht eingestellt bleiben. Mitarbeiter die für Teilzeit, Remote oder eine Auftragsarbeit eingestellt werden sind kostenlos, das gilt auch für Praktikanten." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: autosave: "Sichere Änderungen automatisch" me_tab: "Ich" picture_tab: "Bild" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" upload_picture: "Ein Bild hochladen" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Passwort" emails_tab: "Emails" admin: "Admin" new_password: "Neues Passwort" new_password_verify: "Passwort verifizieren" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "Email Abonnements" email_subscriptions_none: "Keine Email Abonnements." email_announcements: "Ankündigungen" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: job_profile_explanation: "Hi! Fülle dies aus und wir melden uns bei dir bezüglich des Auffindens eines Jobs als Programmierer" sample_profile: "Ein Beispielprofil ansehen" view_profile: "Dein Profil ansehen" - wizard_tab: "Zauberer" - wizard_color: "Die Farbe der Kleidung des Zauberers" keyboard_shortcuts: keyboard_shortcuts: "Tastaturkürzel" space: "Leertaste" enter: "Eingabetaste" +# press_enter: "press enter" escape: "Escape" shift: "Umschalttaste" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: toggle_pathfinding: "Wegfindungs-Overlay an/aus." beautify: "Verschönere deinen Code durch die Standardisierung der Formatierung." maximize_editor: "Maximiere/Minimiere Code Editor." - move_wizard: "Bewege deinen Zauberer durch das Level." community: main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: social_hipchat: "Chatte mit uns in unserem öffentlichen CodeCombat HipChat Raum" contribute_to_the_project: "Trage zu diesem Projekt bei" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "Erzmagier" archmage_title_description: "(Programmierer)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "Handwerker" artisan_title_description: "(Level Entwickler)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "Abenteurer" adventurer_title_description: "(Level Spieltester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Schreiber" scribe_title_description: "(Artikel Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "Diplomat" diplomat_title_description: "(Übersetzer)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "Botschafter" ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: main_title: "CodeCombat Editoren" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: thang_title: "Thang Editor" level_title: "Level Editor" achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" back: "Zurück" revert: "Zurücksetzen" revert_models: "Models zurücksetzen." pick_a_terrain: "Wähle ein Terrain" - small: "Klein" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" grassy: "Grasig" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Klein" +# large: "Large" fork_title: "Forke neue Version" fork_creating: "Erzeuge Fork..." generate_terrain: "Generiere Terrain" more: "Mehr" wiki: "Wiki" live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" level_some_options: "Einige Einstellungsmöglichkeiten?" level_tab_thangs: "Thangs" level_tab_scripts: "Skripte" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: level_tab_thangs_all: "Alle" level_tab_thangs_conditions: "Startbedingungen" level_tab_thangs_add: "Thangs hinzufügen" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" delete: "Löschen" duplicate: "Duplizieren" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" level_settings_title: "Einstellungen" level_component_tab_title: "Aktuelle Komponenten" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: new_level_title_login: "Melde dich an um ein neues Level zu erstellen" new_achievement_title: "Erstelle ein neues Achievement" new_achievement_title_login: "Melde dich an um ein neues Achievement zu erstellen" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" article_search_title: "Durchsuche Artikel hier" thang_search_title: "Durchsuche Thang-Typen hier" level_search_title: "Durchsuche Levels hier" achievement_search_title: "Durchsuche Achievements" +# poll_search_title: "Search Polls" read_only_warning2: "Warnung: Du kannst hier keine Änderungen speichern, weil du nicht angemeldet bist." no_achievements: "Es wurden noch keine Achievements zu diesem Level hinzugefügt." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Vorschau" edit_article_title: "Artikel bearbeiten" +# polls: +# priority: "Priority" + contribute: # page_title: "Contributing" - character_classes_title: "Charakter Klassen" - introduction_desc_intro: "Wir haben hohe Erwartungen für CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " - introduction_desc_github_url: "CodeCombat ist komplett OpenSource" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." - introduction_desc_ending: "Wir hoffen du nimmst an unserer Party teil!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" alert_account_message_intro: "Hey du!" alert_account_message: "Um Klassen-Emails abonnieren zu können, musst du dich zuerst anmelden." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." class_attributes: "Klassenattribute" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: join_desc_4: "und wir schauen von dort mal!" join_url_email: "Emaile uns" join_url_hipchat: "öffentlicher HipChat Raum" - more_about_archmage: "Erfahre mehr darüber wie du ein Erzmagier werden kannst" archmage_subscribe_desc: "Erhalte Emails über neue Programmier-Möglichkeiten und Ankündigungen." - artisan_summary_pref: "Du möchtest Levels erstellen und CodeCombats Arsenal erweitern? Unsere Nutzer spielen unseren Content schneller durch als wir ihn erstellen können! Momentan ist unser Level-Editor noch minimalistisch, also ist noch Vorsicht geboten. Die Levelerstellung wird noch etwas schwierig und buggy(fehlerbehaftet) sein. Wenn du Ideen für Kampagnen die for-loops umspannen" - artisan_summary_suf: ", dann ist diese Klasse für dich." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: artisan_join_step2: "Erstelle ein neues Level und erkunde existierende Level." artisan_join_step3: "Finde uns im öffentlichen HipChat Raum, falls du Hilfe brauchst." artisan_join_step4: "Poste deine Level im Forum um Feedback zu erhalten." - more_about_artisan: "Erfahre mehr darüber wie du ein Handwerker werden kannst" artisan_subscribe_desc: "Erhalte Emails über Level-Editor Updates und Ankündigungen." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" - more_about_adventurer: "Erfahre mehr darüber wie du ein Abenteurer werden kannst" adventurer_subscribe_desc: "Erhalte Emails wenn es neue Levels zum Testen gibt." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." contact_us_url: "Kontaktiere uns" scribe_join_description: "erzähle uns ein bißchen über dich, deine Erfahrung mit der Programmierung und über welche Themen du schreiben möchtest. Wir werden von dort aus gehen!" - more_about_scribe: "Erfahre mehr darüber wie du ein Schreiber werden kannst" scribe_subscribe_desc: "Erhalte Emails über Ankündigungen zu schreibenden Artikeln." - diplomat_summary: "Es herrscht ein großes Interesse an CodeCombat in anderen Ländern die kein Englisch sprechen! Wir suchen nach Übersetzern die gewillt sind ihre Zeit mit der Übersetzung der Webseite zu verbringen, so dass CodeCombat so schnell wie möglich für alle weltweit zugänglich ist. Wenn du helfen möchtest CodeCombat International zugänglich zu machen, dann ist diese Klasse für dich." diplomat_introduction_pref: "Also wenn es eines gibt was wir gelernt haben vom " diplomat_launch_url: "Launch im Oktober" diplomat_introduction_suf: "ist das es ein großes Interesse an CodeCombat in anderen Ländern gibt! Wir stellen eine Truppe von Übersetzern zusammen, die gewillt sind einen Satz Wörten in einen anderen Satz Wörter umzuwandeln um CodeCombat der Welt so zugänglich wie möglich zu machen. Wenn du es magst eine Vorschau von zukünftigem Content zu erhalten und diese Level so schnell wie möglich deinen Landsleuten zur Verfügung zu stellen, dann ist diese Klasse vielleicht für dich." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: diplomat_join_pref_github: "Finde deine Sprachdatei " diplomat_github_url: "bei GitHub" diplomat_join_suf_github: ", editiere sie online und reiche einen Pull Request ein. Außerdem, hake die Checkbox unten an um über neue Entwicklungen bei der Internationalisierung auf dem laufenden zu bleiben!" - more_about_diplomat: "Erfahre mehr darüber wie du ein Diplomat werden kannst" diplomat_subscribe_desc: "Erhalte Emails über i18n Entwicklungen und Level die übersetzt werden müssen." - ambassador_summary: "Wir versuchen eine Community aufzubauen und jede Community braucht ein Support-Team wenn es Probleme gibt. Wir haben Chats, Emails und soziale Netzwerke sodass unsere Benutzer mit dem Spiel vertraut werden können. Wenn du dabei helfen möchtest Leute zu animieren, Spass zu haben und programmieren zu lernen, dann ist diese Klasse für dich." ambassador_introduction: "Wir bauen einen Community und du bist die Verbindung dazu. Wir haben Olark Chats, Email und soziale Netzwerke mit vielen Menschen mit denen man sprechen, dabei helfen mit dem Spiel vertraut zu werden und von lernen kann. Wenn du helfen möchtest Leute zu involvieren, Spass zu haben und ein gutes Gefühl für den Puls von CodeCombat und wo wir hn wollen, dann könnte diese Klasse für dich sein." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" ambassador_join_note_strong: "Anmerkung" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" - more_about_ambassador: "Erfahre mehr darüber wie du ein Botschafter werden kannst" ambassador_subscribe_desc: "Erhalte Emails über Support-Updates and Mehrspieler-Entwicklungen." changes_auto_save: "Änderungen an Checkboxen werden automatisch gespeichert." diligent_scribes: "Unsere fleißgen Schreiber:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: fight: "Kämpft!" watch_victory: "Schau dir deinen Sieg an" defeat_the: "Besiege den" +# tournament_started: ", started" tournament_ends: "Turnier endet" tournament_ended: "Turnier beendet" tournament_rules: "Turnier-Regeln" tournament_blurb: "Schreibe Code, sammle Gold, erstelle Armeen, zerquetsche Feinde, gewinne Preis und verbessere deine Karriere in unserem 40.000 $ Greed-Turnier! Schau dir die Details" tournament_blurb_criss_cross: "Gewinne Gebote, konstruiere Pfade, trickse Feinde aus, greife Edelsteine ab und verbessere deine Karriere in unserem Criss-Cross-Turnier! Schau dir die Details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" tournament_blurb_blog: "auf unserem Blog an" rules: "Regeln" winners: "Gewinner" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: no_achievements: "Noch keine Achievements verdient." favorite_prefix: "Lieblingssprache ist " favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: account: recently_played: "Kürzlich gespielt" no_recent_games: "Keine Spiele in den letzten zwei Wochen gespielt." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "Fehler beim Laden vom Server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: leaderboard: "Rangliste" user_schema: "Benutzerschema" user_profile: "Benutzerprofil" +# patch: "Patch" patches: "Patche" # patched_model: "Source Document" model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: user_remarks: "Benutzerkommentare" versions: "Versionen" items: "Gegenstände" +# hero: "Hero" heroes: "Helden" - wizard: "Zauberer" achievement: "Achievement" clas: "CLAs" # play_counts: "Play Counts" feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" delta: added: "hinzugefügt" modified: "modifiziert" +# not_modified: "Not Modified" deleted: "gelöscht" # moved_index: "Moved Index" text_diff: "Text Diff" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: practices_title: "Best Practices" practices_description: "Dies sind unsere Versprechen an dich, den Spieler, in weniger Fachchinesisch." privacy_title: "Datenschutz" - privacy_description: "Wir werden deine persönlichen Daten nicht verkaufen. Letztenendes beabsichtigen wir, durch Vermittlung von Jobs zu verdienen, aber sei versichert, dass wir nicht deine persönlichen Daten ohne deine ausdrückliche Einwilligung interessierten Firmen zur Verfügung stellen werden." +# privacy_description: "We will not sell any of your personal information." security_title: "Datensicherheit" security_description: "Wir streben an, deine persönlichen Daten sicher zu verwahren. Als Open Source Projekt ist unsere Site frei zugänglich für jedermann, auch um unsere Sicherheitsmaßnahmen in Augenschein zu nehmen und zu verbessern." email_title: "Email" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: email_description_suffix: "oder durch von uns gesendete Links kannst du jederzeit deine Einstellungen ändern und Abonnements kündigen." cost_title: "Kosten" cost_description: "CodeCombat ist zur Zeit 100% kostenlos! Eines unserer Hauptziele ist, es dabei zu belassen, so dass es so viele Leute wie möglich spielen können, unabhängig davon in welcher Lebenssituation sie sich befinden. Falls dunkle Wolken aufziehen, könnten wir manche Inhalte im Rahmen eines Abonnements anbieten, aber lieber nicht. Mit etwas Glück können wir die Firma erhalten durch:" - recruitment_title: "Recruiting" - recruitment_description_prefix: "Hier bei CodeCombat kannst du ein mächtiger Zauberer werden, nicht nur im Spiel, sondern auch in der Realität." - url_hire_programmers: "Niemand kann schnell genug Programmierer einstellen." - recruitment_description_suffix: "So wenn du deine Fähigkeiten entwickelt hast und zustimmst, werden wir deine besten Leistungen den tausenden Arbeitgebern demonstrieren, welche nur auf die Gelegentheit warten, dich einzustellen. Sie bezahlen uns ein bisschen, und sie bezahlen dir " - recruitment_description_italic: "jede Menge" - recruitment_description_ending: ", die Seite bleibt kostenlos und jeder ist glücklich. So der Plan." copyrights_title: "Copyrights und Lizenzen" contributor_title: "Contributor License Agreement" contributor_description_prefix: "Alle Beiträge, sowohl auf unserer Webseite als auch in unserem GitHub Repository, unterliegen unserer" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: license: "Lizenz" oreilly: "Ebook deiner Wahl" - wizard_settings: - title: "Zauberer Einstellungen" - customize_avatar: "Individualisiere deinen Avatar" - active: "Aktiv" - color: "Farbe" - group: "Gruppe" - clothes: "Kleidung" - trim: "Applikationen" - cloud: "Wolke" - team: "Team" - spell: "Zauber" - boots: "Stiefel" - hue: "Farbton" - saturation: "Sättigung" - lightness: "Helligkeit" - account_profile: settings: "Einstellungen" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Profil editieren" diff --git a/app/locale/de-CH.coffee b/app/locale/de-CH.coffee index 3ada8310f..1b2dfd28d 100644 --- a/app/locale/de-CH.coffee +++ b/app/locale/de-CH.coffee @@ -1,16 +1,17 @@ -module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "German (Switzerland)", translation: +module.exports = nativeDescription: "Dütsch (Schwiiz)", englishDescription: "German (Switzerland)", translation: home: - slogan: "Lern, wiemer JavaScript programmiert indem du es Spiel spielsch!" + slogan: "Lern, wiemer JavaScript programmiert, indem du spielsch!" no_ie: "CodeCombat funktioniert uf InternetExplorer 8 und älter nid. Sorry!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat isch nid für mobili Grät entwicklet worde und funktioniert vilicht nid!" # Warning that shows up on mobile devices - play: "Spiele" # The big play button that just starts playing a level + play: "Spiele" # The big play button that opens up the campaign view. old_browser: "Uh oh, din Browser isch z alt zum CodeCombat spiele. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Du chasches gliich probiere, aber es funktioniert worschinli nid." + ipad_browser: "Schächti Nachrichte: CodeCombat funktioniert nonig uf em iPad-Browser. Gueti Nachrichte: Oisi iPad-App wartet nur no druf, vo Apple überprüeft z werde." campaign: "Kampagne" for_beginners: "Für Afänger" multiplayer: "Multiplayer" # Not currently shown on home page for_developers: "Für Entwickler" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Oder lads fürs iPad abä" nav: play: "Levels" # The top nav bar entry where players choose which levels to play @@ -19,9 +20,9 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge blog: "Blog" forum: "Forum" account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + profile: "Profil" + stats: "Statistike" + code: "Code" admin: "Admin" # Only shows up when you are an admin home: "Home" contribute: "Mitmache" @@ -29,7 +30,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge about: "Über" contact: "Kontakt" twitter_follow: "Folge" -# teachers: "Teachers" + teachers: "Lehrer" modal: close: "Beende" @@ -49,84 +50,84 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge play: play_as: "Spiel als" # Ladder page spectate: "Zueluege" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + players: "Spieler" # Hover over a level on /play + hours_played: "Stunde gspilt" # Hover over a level on /play + items: "Items" # Tooltip on item shop button from /play + unlock: "Freischalte" # For purchasing items and heroes + confirm: "Bestätige" + owned: "Scho gkauft" # For items you own + locked: "Nonig kauft" + purchasable: "kaufen" # For a hero you unlocked but haven't purchased + available: "vorhandä" + skills_granted: "Fähigkeite" # Property documentation details + heroes: "Helde" # Tooltip on hero shop button from /play + achievements: "Erfolg" # Tooltip on achievement list button from /play + account: "Account" # Tooltip on account button from /play + settings: "Istellige" # Tooltip on settings button from /play + poll: "Pool" # Tooltip on poll button from /play + next: "Wiiter" # Go from choose hero to choose inventory before playing a level + change_hero: "Held wächsle" # Go back from choose inventory to choose hero + choose_inventory: "Items uusrüschte" + buy_gems: "Edelstei chaufä" + subscription_required: "Abonnement benötigt" + anonymous: "Anonyme Spieler" level_difficulty: "Schwierigkeit: " campaign_beginner: "Afängerkampagne" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Wähl dis Level us" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Du chasch zu de untere Level zrugg goh oder die kommende Level diskutiere im " - adventurer_forum: "Abentürer-Forum" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... i dere du d Zauberkunst vom Programmiere lernsch." - campaign_dev: "Zuefälligi schwierigeri Level" - campaign_dev_description: "... i dene du s Interface kenne lernsch, während du öppis chli Schwierigers machsch." + awaiting_levels_adventurer_prefix: "Mier möched 5 Levels pro Wuche" # {change} + awaiting_levels_adventurer: "Mäld dich a as en Abendtüürer" + awaiting_levels_adventurer_suffix: "um de erscht zii vo die neue Levels spiilt" + adjust_volume: "Luutsterchi apasse" campaign_multiplayer: "Multiplayer Arenas" campaign_multiplayer_description: "... i dene du Chopf a Chopf geg anderi Spieler spielsch." - campaign_player_created: "Vo Spieler erstellti Level" - campaign_player_created_description: "... i dene du gege d Kreativität vome <a href=\"/contribute#artisan\">Handwerker Zauberer</a> kämpfsch." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Du machsch grossi Fortschritts! Verzells öperem wieviel du glernt häsch mit CodeCombat." + email_invalid: "Email Adrässä isch falsch." + form_blurb: "Gib bitte dEmail Adrässe vo dine Eltere aa" + form_label: "Email Adrässä" + placeholder: "Email Adrässä" + title: "Gueti Arbeit!" login: sign_up: "Account erstelle" log_in: "Ilogge" logging_in: "Am Ilogge" log_out: "Uslogge" - recover: "Account wiederherstelle" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Passwort vergässe?" + authenticate_gplus: "Mit G+ audentifiziere" + load_profile: "G+ Profil ladä" + finishing: "Fertigstelle" + sign_in_with_facebook: "Mit Facebook aamelde" + sign_in_with_gplus: "Mit G+ aamelde" + signup_switch: "Willsch es Account erstelle?" signup: - create_account_title: "Erstell en Account zum din Fortschritt speichere" - description: "Es isch gratis. Nur no es paar Sache und denn chas los goh:" email_announcements: "Akündigunge per Mail erhalte" - coppa: "13+ or Nicht-Amerikaner " - coppa_why: "(Warum?)" creating: "Account wird erstellt..." sign_up: "Registriere" log_in: "Mit Passwort ilogge" social_signup: "Du chasch dich au mit Facebook oder G+ registriere:" required: "Du muesch dich zersch ilogge befor du det dure chasch" + login_switch: "Häsch scho es Account?" recover: recover_account_title: "Account wiederherstelle" - send_password: "Recovery Password sende" -# recovery_sent: "Recovery email sent." + send_password: "Widerherstelligs Passwort sende" + recovery_sent: "Widerherstelligs Passwort isch gsendet" -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Primär" + secondary: "Sekundär" + armor: "Rüschtig" + accessories: "Accessories" + misc: "Diverses" + books: "Büecher" common: + back: "Zrugg" # When used as an action verb, like "Navigate backward" + continue: "Wiiterfare" # When used as an action verb, like "Continue forward" loading: "Lade..." saving: "Speichere..." sending: "Sende..." @@ -139,41 +140,62 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # fork: "Fork" play: "Spiele" # When used as an action verb, like "Play next level" retry: "nomol versuche" -# watch: "Watch" -# unwatch: "Unwatch" + actions: "Aktione" + info: "Info" + help: "Hilf" + watch: "Aluege" + unwatch: "Nüm Aluege" submit_patch: "Patch ireiche" + submit_changes: "Wechsel ireiche" +# save_changes: "Save Changes" -# general: -# and: "and" -# name: "Name" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" -# or: "or" + general: + and: "und" + name: "Name" + date: "Datum" + body: "Body" + version: "Version" + pending: "in Bearbeitig" + accepted: "Akzeptiert" + rejected: "Nid akzeptiert" + withdrawn: "Zruggzie" + submitter: "Sender" + submitted: "Gesendet" + commit_msg: "Nachricht abschicke" + review: "Review" + version_history: "Versionsverlauf" + version_history_for: "Versionsverlauf für: " + select_changes: "Wähl zwei Verändrige um unne Ihre Unterschid zgse." + undo_prefix: "Eis zrugg" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Nomal mache" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Spiel dPreview vom aktuelle Level" + result: "Resultat" + results: "Resultat" + description: "Beschriibig" + or: "oder" # subject: "Subject" -# email: "Email" -# password: "Password" -# message: "Message" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + email: "E-mail" + password: "Passwort" + message: "Nachricht" + code: "Code" + ladder: "Leitere" + when: "Wänn" + opponent: "Gegner" + rank: "Rang" + score: "Punktzahl" + win: "Gwünn" + loss: "Verlust" + tie: "Unentschide" + easy: "Eifach" + medium: "Mittel" + hard: "Schwer" + player: "Spieler" + player_level: "Stufe" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Krieger" + ranger: "Ranger" + wizard: "Zauberer" units: second: "Sekunde" @@ -194,76 +216,84 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge play_level: done: "Fertig" home: "Home" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "Level" # Like "Level: Dungeons of Kithgard" + skip: "Überspringe" + game_menu: "Game Menu" guide: "Aleitig" restart: "Neu starte" goals: "Ziel" -# goal: "Goal" -# running: "Running..." + goal: "Goal" + running: "s lauft..." success: "Erfolg!" incomplete: "Unvollständig" timed_out: "Ziit abglaufe" failing: "Fehler" action_timeline: "Aktionsziitleiste" click_to_select: "Klick uf e Einheit zum sie uswähle." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "Mehrspiiler" + control_bar_join_game: "Mitspiilä" + reload: "Neu lade" reload_title: "De ganze Code neu lade?" reload_really: "Bisch sicher du willsch level neu lade bis zrugg zum Afang?" reload_confirm: "Alles neu lade" -# victory_title_prefix: "" + victory: "Gwunne" + victory_title_prefix: "" victory_title_suffix: " Vollständig" victory_sign_up: "Meld dich ah zum din Fortschritt speichere" victory_sign_up_poke: "Wötsch din Code speichere? Erstell gratis en Account!" victory_rate_the_level: "Bewerte das Level: " # Only in old-style levels. victory_return_to_ladder: "Zrugg zum letzte Level" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Spiel s nögste Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" -# victory_go_home: "Go Home" # Only in old-style levels. + victory_play_continue: "Wiiter spile" + victory_saving_progress: "Fortschritt abspaicherä" + victory_go_home: "Goon Hai" # Only in old-style levels. victory_review: "Verzell üs meh!" # Only in old-style levels. victory_hour_of_code_done: "Bisch fertig?" victory_hour_of_code_done_yes: "Jo, ich bin fertig mit mim Hour of Code™!" + victory_experience_gained: "Erfarig bechoo" + victory_gems_gained: "Edelstei bechoo" +# victory_new_item: "New Item" + victory_viking_code_school: "Oh mein Gott, dass isch aber es stregs Level gsi und du heschs gschafft! Also wen du nu kei Software-Entwickler bisch, sötsch eine sii! Du hesch en Iiladig becho um at Viking Code Schuel zgha wodu dini Fähigkeite chasch wiiterentwickle und en professionele Entwickel in nur 14 Täg werde!" + victory_become_a_viking: "Werd en Vikinger!" guide_title: "Handbuech" tome_minion_spells: "Zaubersprüch vo dine Minions" # Only in old-style levels. tome_read_only_spells: "Read-Only Zaubersprüch" # Only in old-style levels. tome_other_units: "Anderi Einheite" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_run: "Renn" + tome_cast_button_running: "Renne" + tome_cast_button_ran: "grennt" + tome_submit_button: "Abschicke" + tome_reload_method: "Lad de Orginal Code für die Methode" # Title text for individual method reload button. + tome_select_method: "Wähl a Methodä" + tome_see_all_methods: "Lueg alli Methode a wot chasch bearbeite" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Wähl öpper us für" tome_available_spells: "Verfüegbari Zaubersprüch" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_your_skills: "Dini Fähigkaitä" + tome_help: "Hilf" + tome_current_method: "Aktuelli Modus" + hud_continue_short: "Wiitermache" + code_saved: "Code gpeicheret" skip_tutorial: "Überspringe (esc)" keyboard_shortcuts: "Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" + loading_ready: "Berait!" + loading_start: "Level starte" + problem_alert_title: "Reparier diin Code" + problem_alert_help: "Hilf" time_current: "Jetzt:" time_total: "Max:" time_goto: "Goh zu:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Versuechs nomol" infinite_loop_reset_level: "Level zrugsetze" infinite_loop_comment_out: "Min Code uskommentiere" tip_toggle_play: "Play/Pausiert mit Ctrl+P ischalte." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." tip_guide_exists: "Klick ufs Handbuech im obere Teil vo de Siite zum nützlichi Infos becho." tip_open_source: "CodeCombat isch 100% Open Source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "D CodeCombat Beta isch im Oktober 2013 online gange." tip_think_solution: "Denk über d Lösig noh, nid über s Problem." tip_theory_practice: "Theoretisch gits kein Unterschied zwüsche Theorie und Praxis. Praktisch aber scho. - Yogi Berra" @@ -285,52 +315,161 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge tip_impossible: "Es schiint immer unmöglich bis es gschafft isch. - Nelson Mandela" tip_talk_is_cheap: "Rede isch billig. Zeig mir de Code. - Linus Torvalds" tip_first_language: "S Katastrophalste wo du chasch lerne, isch dini erst Programmiersproch. - Alan Kay" - tip_hardware_problem: "Q: Wie viel Programmierer bruuchts zum e Glüehbire uswechsle? A: Keine, da isch es Hardware Problem." + tip_hardware_problem: "F: Wie viel Programmierer bruuchts zum e Glüehbire uswechsle? A: Keine, da isch es Hardware Problem." # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Zauberer apasse" + tip_brute_force: "Went am verzwifle bisch, bruch brute force. - Ken Thompson" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." + tip_superpower: "Coding isch snöchte wo mier hend was ane Superchraaft ane chund!" +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" + tip_no_code: "Kei Code isch schneller als kei Code!" +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" + tip_source_code: "Ich wet dWält verändere aber die wend mier de Source Code nid gää." + tip_javascript_java: "Java isch zu JavaScript wie es Auto zume Automat. - Chris Heilmann" + tip_move_forward: "Was immer du machsch, mach immer me Fortschritt. - Martin Luther King Jr." + tip_google: "Hesch es Problem und chunsch nüm wiiter? Googles doch mal!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" + tip_open_source_contribute: "Du chasch helfe CodeCombat zverbessere!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "Inventar" + save_load_tab: "Spaicherä/Ladä" + options_tab: "Optionä" + guide_tab: "Guide" + guide_video_tutorial: "Vidio Tutorial" + guide_tips: "Tipps" multiplayer_tab: "Multiplayer" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + auth_tab: "Regischtriere" + inventory_caption: "Rüscht din Held uus" + choose_hero_caption: "Wähl din Held und dini Sprach" + save_load_caption: "... und lueg dini Gschicht aa." + options_caption: "Iistellige apasse" + guide_caption: "Doku und Tipps" + multiplayer_caption: "Spil mid dini Fründe!" + auth_caption: "Speichere din Fortschritt." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Ranglischte" + view_other_solutions: "Lueg der dRanglischte aa!" + scores: "Pünkt" + top_players: "Beschti Speiler" + day: "Hüt" + week: "Die Wuuche" + all: "Vo immer" + time: "Ziit" + damage_taken: "Schade gnoo" + damage_dealt: "Schade uusteilt" + difficulty: "Schwirigkeitsgrad" + gold_collected: "Gold gsammlet" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "Items uusrüschte" + equipped_item: "Uusgrüschteti Items" + required_purchase_title: "Benötigt" + available_item: "Verfüegbar" + restricted_title: "Verbote" + should_equip: "(2mal Klicke zum uusrüschte)" + equipped: "(usgrüschtet)" + locked: "(geschperrt)" + restricted: "(verbote i dem Level)" + equip: "Uusrüschte" + unequip: "Nüm Uusrüschte" + + buy_gems: + few_gems: "Es paar Edelstei" + pile_gems: "En hufe vo Edelstei" + chest_gems: "En ganzi True voll Edelstei" + purchasing: "Kaufen..." + declined: "Dini Charte isch leider abglehnt worde." + retrying: "Server Fehler, probiere nochmals." + prompt_title: "Nid gnug Edelstei!" + prompt_body: "Wetsch mee chaufe?" + prompt_button: "zum Shop" + recovered: "Früenere Ichauf zruggerstatet. Bitte dSite neu lade!" +# price: "x3500 / mo" + + subscribe: + comparison_blurb: "Verschärf dins Chönne midme CodeCombat Abonement." + feature1: "80+ basis levels in 4 Weltete!" # {change} +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" + feature3: "50+ bonus levels" # {change} +# feature4: "<strong>3500 bonus gems</strong> every month!" + feature5: "Video Aleitige" + feature6: "Premium Email Hilf" +# feature7: "Private <strong>Clans</strong>" + free: "Gratis" + month: "Monät" + subscribe_title: "Aboniere" + unsubscribe: "Deaboniere" + confirm_unsubscribe: "Deaboniere beschtätige" + never_mind: "Keis Problem, ich lieb dich trotzdem." + thank_you_months_prefix: "Danke das du üs die Monät" + thank_you_months_suffix: "so unterschtütz hesch." + thank_you: "Danke, dass du CodeCombat so unterschtüzisch." + sorry_to_see_you_go: "Schad, dass du gasch! Bitte seg üs doch was mier hetted chönne besser mache." + unsubscribe_feedback_placeholder: "Ohaletz, was hemmer gmacht?" + parent_button: "Frag dini Eltere" + parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." + parent_email_input_invalid: "Email Adrässi ungültig" + parent_email_input_label: "Email Adrässi vo dine Eltere" + parent_email_input_placeholder: "Bitte gib dMail Adrässi vo dine Eltere a" + parent_email_send: "Email sände" + parent_email_sent: "Email gsändet!" + parent_email_title: "Wie isch dEmail Adrässi vo dine Eltere?" + parents: "Für Eltere" + parents_title: "Liebi Eltere, Ihres Chind isch am Lerne wieme programmiert. Wen Sie im helfe?" + parents_blurb1: "Ihres CHind hed __nLevels__ levels gschpilt und hed programmier Basics glernd. Helfed Sie soch dInträssi für sProgrammiere ufrecht zhalte und unterschtützed Sie in idem Sie es Abo chaufed." + parents_blurb1a: "Programmiere isch en wichtigi Begabig wo Ihres Chind als Erwachsene sicher wird bruche. Ab 2020 werded 77% vo allne Jobs eifachi Programmierkentniss benötige und die wos behersched werded uf de ganze Welt gsuecht. Hend Sie gwüsst das Programmiere de best bezallti Uni abschluss isch?" + parents_blurb2: "Für $9.99 USD/im Monät, würd Ihres Chind jedes Wuche neue und spannendi Challenges becho und professionelle E-Mail Support!" + parents_blurb3: "Kei Risikos, 100% Geld zrugg Garantie und ganz eifaches deaboniere mid eim Klick" + payment_methods: "Zalligs Methode" + payment_methods_title: "Akzeptierti Zalligs Methode" + payment_methods_blurb1: "Mier akzeptiered immoment Kreditchartene und Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" + subscription_required_to_play: "Du bruchsch es Abo um das Level zspile." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below + loading_info: "Lade Abo Informatione..." + managed_by: "Verwaltet vo" + will_be_cancelled: "Wird abbroche am" + currently_free: "Du hesch jetzt grad es frii Abo" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" + managed_subs: "Abos verwalte" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." + group_discounts_1st: "Erstes Abonement" + group_discounts_full: "de ganz Priis" + group_discounts_2nd: "Aboniere 2-11" + group_discounts_20: "20% billiger" + group_discounts_12th: "Aboniere 12+" + group_discounts_40: "40% billiger" + subscribing: "Am Aboniere..." + recipient_emails_placeholder: "Gib dini Mails zum Aboniere i, eine pro Linie:" + subscribe_users: "Abonier Users" + users_subscribed: "Users aboniert:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" + unsubscribing: "Am Abo chünde" + subscribe_prepaid: "Klick Aboniere um en PrePaid Code izlöse" + using_prepaid: "Bruch en PrePaid Code um en Monet zAboniere" choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" + choose_hero: "Wähl din Held" + programming_language: "Programmiersprach" + programming_language_description: "Weli Programmiersprach wetsch benutze?" + default: "Standard" # experimental: "Experimental" python_blurb: "Eifach und doch mächtig." javascript_blurb: "D Internetsproch." @@ -339,27 +478,40 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge lua_blurb: "D Sproch für Game Scripts." io_blurb: "Eifach aber undurchsichtig." # status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" + hero_type: "Typ" + weapons: "Waffene" + weapons_warrior: "Schwärter - churzi Richwiti, kei Magie" + weapons_ranger: "Armbrüscht, Knarre - grossi Richwiti, kei Magie" + weapons_wizard: "Zauberstäb - grossi Richwiti, Magie" + attack: "Schadä" # Can also translate as "Attack" + health: "Läbä" + speed: "Schnelligkeit" + regeneration: "Regeneration" + range: "Richwiti" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" + skills: "Fähigkeite" +# attack_1: "Deals" +# attack_2: "of listed" + attack_3: "Waffeschade." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." -# skill_docs: + skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this # read_only: "read-only" -# action_name: "name" + action_name: "name" # action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" + action_specific_cooldown: "Abklingziit" + action_damage: "Schade" + action_range: "Richwiti" + action_radius: "Radius" # action_duration: "Duration" # example: "Example" # ex: "ex" # Abbreviation of "example" @@ -369,17 +521,15 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # returns: "Returns" # granted_by: "Granted by" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + save_load: + granularity_saved_games: "Gschpeicheret" + granularity_change_history: "Verlauf" options: # general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" + volume_label: "Luutstärchi" + music_label: "Musig" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" editor_config_title: "Editor Konfiguration" editor_config_level_language_label: "Sproch für das Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" + michael_title: "Programmierer" + michael_blurb: "System Admin" + matt_title: "Programmierer" # {change} # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" + cat_blurb: "Luftbändiger" + josh_title: "Game Designer" + josh_blurb: "De Bode isch Lava" + jose_title: "Musig" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat: Info für Lehrer" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Neui Version speichere" new_major_version: "Neui Hauptversion" + submitting_patch: "Patch am abgee" # cla_prefix: "To save changes, first you must agree to our" -# cla_url: "CLA" -# cla_suffix: "." -# cla_agree: "I AGREE" + cla_url: "CLA" + cla_suffix: "." + cla_agree: "Ich bi iverstandee" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "CodeCombat kontaktiere" welcome: "Mir ghöred gern vo dir! Benutz das Formular zum üs e E-Mail schicke." - contribute_prefix: "Wenn du dra interessiert bisch, mitzhelfe denn lueg doch mol verbii uf üsere " - contribute_page: "Contribute Page" - contribute_suffix: "!" forum_prefix: "Für öffentlichi Sache versuechs mol stattdesse i " forum_page: "üsem Forum" forum_suffix: "." + faq_prefix: "Es gid au es" + faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Feedback schicke" contact_candidate: "Kandidat kontaktiere" # Deprecated recruitment_reminder: "Benutz das Formular zum mit Kandidate Kontakt ufneh, i die du interessiert bisch. Bhalt in Erinnerig, dass CodeCombat 15% vom erstjöhrige Lohn verrechnet. De Betrag wird fällig, sobald de Programmierer agstellt wird und chan 90 Täg lang zruggverrechnet werde wenn de Agstellti nid agstellt bliibt. Teilziitarbeit, Fernarbeit und temporäri Agstellti sind chostelos, s gliiche gilt für Interni Mitarbeiter." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge autosave: "Änderige werded automatisch gspeicheret" me_tab: "Ich" picture_tab: "Bild" + delete_account_tab: "Din Account lösche" + wrong_email: "Falschi Email Adrässe" +# wrong_password: "Wrong Password" upload_picture: "Es Bild ufelade" + delete_this_account: "Dä Account für immer Lösche" + god_mode: "Gott Modus" password_tab: "Passwort" emails_tab: "E-Mails" admin: "Admin" new_password: "Neus Passwort" new_password_verify: "Bestätige" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "E-Mail Abos" # email_subscriptions_none: "No Email Subscriptions." email_announcements: "Akündigunge" @@ -464,7 +715,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge email_notifications_summary: "Istellige für personalisierti, automatischi E-Mail Notifikatione im Zemehang mit dine CodeCombat Aktivitäte" email_any_notes: "Alli Notifikatione" email_any_notes_description: "Deaktiviere zum kei Aktivitäts-Notifikatione meh per E-Mail becho." -# email_news: "News" + email_news: "Neuigkeite" # email_recruit_notes: "Job Opportunities" # email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." # contributor_emails: "Contributor Class Emails" @@ -480,18 +731,17 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - wizard_tab: "Zauberer" - wizard_color: "Zaubererchleid Farb" + view_profile: "Dis Profil aluege" keyboard_shortcuts: keyboard_shortcuts: "Shortcuts uf de Tastatur" space: "Space" enter: "Enter" +# press_enter: "press enter" escape: "Escape" -# shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." + shift: "Shift" + run_code: "De jetzig Code laufe laa." + run_real_time: "In Echtziit laufe laa." continue_script: "Nochem aktuelle Script fortsetze." skip_scripts: "Alli überspringbare Scripts überspringe." toggle_playback: "Play/Pause istelle." @@ -502,8 +752,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge toggle_grid: "Gitter ischalte/usschalte." toggle_pathfinding: "Wegfinder ischalte/usschalte." beautify: "Mach din Code schöner, indem du sini Formatierig standartisiersch." -# maximize_editor: "Maximize/minimize code editor." - move_wizard: "Beweg din Zauberer durs Level." + maximize_editor: "Maximize/minimize de code editor." community: main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge practices_title: "Respektvolli bewährti Praxis" practices_description: "Das sind üsi Verspreche a dich, de Spieler, in bitz weniger Fachchinesisch." privacy_title: "Dateschutz" - privacy_description: "Mir verchaufed kei vo dine persönliche Informatione. Mir hend vor zum irgendwenn durch Rekrutierig Geld z verdiene, aber bis versicheret, dass mir nid dini persönliche Date a interessierti Firmene wiiter gebed ohni dis usdrücklich Iverständnis." +# privacy_description: "We will not sell any of your personal information." security_title: "Sicherheit" security_description: "Mir bemühed üs, dini persönliche Informatione sicher ufzbewahre. Als es Open Source Projekt isch üsi Siite offe für jede, wo gern möcht üsi Security System besichtige und verbessere." email_title: "E-Mail" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge email_description_suffix: "oder dur d Links i de E-Mails wo mir schicked, chasch du jederziit dini Preferänze ändere und dich ganz eifach us de Mailing-Liste neh." cost_title: "Chöste" cost_description: "Im Moment isch CodeCombat 100% gratis! Eis vo üsne Hauptziel isch, dass das so bliibt, damit so viel Lüüt wie möglich chönd spiele, egal wo sie sich im Lebe befinded. Sötted dunkli Wolke am Horizont ufzieh chas sii, dass mir müed en Teil vom Inhalt chostepflichtig mache, aber es isch üs lieber, wenn da nid passiert. Mit chli Glück werded mir fähig sii, s Unternehme ufrecht z erhalte und zwor mit:" - recruitment_title: "Rekrutierig" - recruitment_description_prefix: "Do uf CodeCombat wirsch du en mächtige Zauberer - nid nur ingame, sonder au im echte Lebe." - url_hire_programmers: "Niemer cha Programmierer schnell gnueg astelle" - recruitment_description_suffix: "das heisst, sobald du dini Fähigkeite gschärft hesch, und wenn du zuestimmsch, werded mir dini beste Programmiererfolg de tuusige vo Arbeitgeber zeige, wo nur druf warted, dich chöne azstelle. Sie zahled üs es bitz öppis, sie zahled dir" - recruitment_description_italic: "ziemli viel" - recruitment_description_ending: "d Siite bliibt gratis und alli sind glücklich. Das isch de Plan." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Zaubereristellige" - customize_avatar: "Pass din Avatar ah" - active: "Aktiv" - color: "Farb" - group: "Gruppe" - clothes: "Chleider" - trim: "Deko" - cloud: "Wolke" - team: "Team" - spell: "Zauberspruch" - boots: "Stiefel" - hue: "Färbig" - saturation: "Sättigung" - lightness: "Helligkeit" - account_profile: settings: "Istellige" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Profil bearbeite" @@ -1070,7 +1412,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # player_code: "Player Code" employers: -# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." + deprecation_warning_title: "Sorry, CodeCombat rekrutiert grad nid." # deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." hire_developers_not_credentials: "Stell Entwickler ah, nid Zügnis." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. # get_started: "Get Started" diff --git a/app/locale/de-DE.coffee b/app/locale/de-DE.coffee index 12ea5069e..965e34457 100644 --- a/app/locale/de-DE.coffee +++ b/app/locale/de-DE.coffee @@ -1,16 +1,17 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: "German (Germany)", translation: home: slogan: "Lerne spielend Programmieren" - no_ie: "CodeCombat läuft nicht im IE8 oder älteren Browsern. Tut uns Leid!" # Warning that only shows up in IE8 and older + no_ie: "CodeCombat läuft nicht im IE 8 oder älteren Browsern. Tut uns leid!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat ist nicht für Mobilgeräte optimiert und funktioniert möglicherweise nicht." # Warning that shows up on mobile devices - play: "Spielen" # The big play button that just starts playing a level + play: "Spielen" # The big play button that opens up the campaign view. old_browser: "Oh! Dein Browser ist zu alt für CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Du kannst es trotzdem versuchen, aber es wird wahrscheinlich nicht funktionieren." + ipad_browser: "Die schlechte Nachricht: CodeCombat läuft auf dem iPad nicht im Browser. Die gute Nachricht: Unsere iPad App wartet gerade auf die Zustimmung von Apple." campaign: "Kampagne" for_beginners: "Für Anfänger" multiplayer: "Mehrspieler" # Not currently shown on home page for_developers: "Für Entwickler" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Oder downloade es hier fürs iPad" nav: play: "Spielen" # The top nav bar entry where players choose which levels to play @@ -41,10 +42,10 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: diplomat_suggestion: title: "Hilf CodeCombat zu übersetzen!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "Wir brauchen Deine Sprachfähigkeiten." - pitch_body: "Wir entwickeln CodeCombat in Englisch, aber wir haben Spieler in der ganzen Welt. Viele von ihnen wollen in Deutsch spielen, sprechen aber kein Englisch. Wenn Du also beide Sprachen beherrscht, melde Dich an um ein Diplomat zu werden und hilf die Website und die Levels zu Deutsch zu übersetzen." - missing_translations: "Solange wir nicht alles ins Deutsche übesetzt haben, siehst Du die englische Übersetzung, wo Deutsch leider noch nicht zur Verfügung steht." - learn_more: "Finde heraus, wie Du ein Diplomat werden kannst" - subscribe_as_diplomat: "Schreibe dich als Diplomat ein" + pitch_body: "Wir entwickeln CodeCombat in Englisch, aber wir haben Spieler in der ganzen Welt. Viele von ihnen wollen auf Deutsch spielen, sprechen aber kein Englisch. Wenn Du also beide Sprachen beherrscht, melde Dich an, um ein Diplomat zu werden und hilf die Website und die Levels ins Deutsche zu übersetzen." + missing_translations: "Solange wir nicht alles ins Deutsche übersetzt haben, siehst Du die englische Übersetzung dort, wo Deutsch leider noch nicht zur Verfügung steht." + learn_more: "Finde heraus, wie Du ein Diplomat werden kannst." + subscribe_as_diplomat: "Schreibe dich als Diplomat ein." play: play_as: "Spiele als " # Ladder page @@ -52,66 +53,64 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: players: "Spieler" # Hover over a level on /play hours_played: "Stunden gespielt" # Hover over a level on /play items: "Gegenstände" # Tooltip on item shop button from /play - unlock: "Entsperren" # For purchasing items and heroes + unlock: "Freischalten" # For purchasing items and heroes confirm: "Bestätigen" - owned: "Besitzen" # For items you own + owned: "im Besitz" # For items you own locked: "Gesperrt" + purchasable: "Zu kaufen" # For a hero you unlocked but haven't purchased available: "Verfügbar" skills_granted: "Verfügbare Fähigkeiten" # Property documentation details heroes: "Helden" # Tooltip on hero shop button from /play achievements: "Erfolge" # Tooltip on achievement list button from /play account: "Account" # Tooltip on account button from /play settings: "Einstellungen" # Tooltip on settings button from /play + poll: "Umfrage" # Tooltip on poll button from /play next: "Nächster" # Go from choose hero to choose inventory before playing a level change_hero: "Held wechseln" # Go back from choose inventory to choose hero choose_inventory: "Gegenstände ausrüsten" buy_gems: "Edelsteine kaufen" - older_campaigns: "Ältere Kampagne" + subscription_required: "Abonnement benötigt" anonymous: "Anonymer Spieler" level_difficulty: "Schwierigkeit: " campaign_beginner: "Anfängerkampagne" - awaiting_levels_adventurer_prefix: "Wir veröffentlichen fünf Levels pro Woche." + awaiting_levels_adventurer_prefix: "Wir veröffentlichen fünf Levels pro Woche." # {change} awaiting_levels_adventurer: "Registriere dich als ein Abenteurer" awaiting_levels_adventurer_suffix: "sei der Erste, der neue Levels spielt." - choose_your_level: "Wähle dein Level" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Du kannst zu jedem Level springen oder diskutiere die Level " - adventurer_forum: "im Abenteurerforum" - adventurer_suffix: "." - campaign_old_beginner: "Alte Anfänger Kampagne" - campaign_old_beginner_description: "... in der Du die Zauberei der Programmierung lernst." - campaign_dev: "Beliebiges schwierigeres Level" - campaign_dev_description: "... in welchem Du die Bedienung erlernst, indem Du etwas schwierigeres machst." - campaign_multiplayer: "Multiplayerarena" + adjust_volume: "Lautstärke anpassen" + campaign_multiplayer: "Mehrspieler Arena" campaign_multiplayer_description: "... in der Du Kopf-an-Kopf gegen andere Spieler programmierst." - campaign_player_created: "Von Spielern erstellt" - campaign_player_created_description: "... in welchem Du gegen die Kreativität eines <a href=\"/contribute#artisan\">Artisan Zauberers</a> kämpfst." - campaign_classic_algorithms: "Klassiche Algorithmen" - campaign_classic_algorithms_description: "... in welchem du die populärsten Algorithmen der Informatik lernst." - campaign_forest: "Forest Kampagne" - campaign_dungeon: "Dungeon Kampagne" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Du machst dich gut! Sag jemanden wie viel du mit CodeCombat gelernt hast." # {change} + email_invalid: "Diese Email Adresse ist ungültig." + form_blurb: "Schreibe hier ihre Emailadresse rein und wir werden es ihnen zeigen." + form_label: "Email Adresse" + placeholder: "Email Adresse" + title: "Gute Arbeit, Lehrling" login: sign_up: "Registrieren" log_in: "Einloggen" logging_in: "Logge ein" log_out: "Ausloggen" - recover: "Account wiederherstellen" + forgot_password: "Passwort vergessen?" authenticate_gplus: "Authentifiziere G+" load_profile: "Lade G+ Profil" - load_email: "Lade G+ Email" finishing: "Fertigstellen" + sign_in_with_facebook: "Melde dich mit Facebook an" + sign_in_with_gplus: "Melde dich mit G+ an" + signup_switch: "Willst du einen Account erstellen?" signup: - create_account_title: "Account anlegen, um Fortschritt zu speichern" - description: "Es ist kostenlos. Nur noch ein paar Dinge, dann kannst Du loslegen." email_announcements: "Erhalte Benachrichtigungen per Email" - coppa: "Älter als 13 oder nicht aus den USA" - coppa_why: "(Warum?)" creating: "Erzeuge Account..." sign_up: "Neuen Account anlegen" log_in: "mit Passwort einloggen" - social_signup: "oder, du registriest dich über Facebook oder G+:" + social_signup: "oder, du registrierst dich über Facebook oder G+." required: "Du musst dich vorher einloggen um dort hin zu gehen." + login_switch: "Du hast schon einen Account?" recover: recover_account_title: "Account Wiederherstellung" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: books: "Bücher" common: + back: "zurückgehen" # When used as an action verb, like "Navigate backward" + continue: "weiter" # When used as an action verb, like "Continue forward" loading: "Lade..." saving: "Speichere..." sending: "Übertrage..." @@ -137,11 +138,16 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: create: "Erstelle" manual: "Manuell" fork: "Fork" - play: "Abspielen" # When used as an action verb, like "Play next level" + play: "Spiel starten" # When used as an action verb, like "Play next level" retry: "Erneut versuchen" - watch: "Verfolgen" - unwatch: "Nicht verfolgen" + actions: "Aktionen" + info: "Info" + help: "Hilfe" + watch: "Beobachten" + unwatch: "Nicht beobachten" submit_patch: "Patch einreichen" + submit_changes: "Änderungen einreichen" +# save_changes: "Save Changes" general: and: "und" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: date: "Datum" body: "Inhalt" version: "Version" - commit_msg: "Commit Nachricht" + pending: "ausstehend" + accepted: "akzeptiert" + rejected: "abgelehnt" + withdrawn: "zurückgezogen" + submitter: "Übermittler" + submitted: "Übermittelt" + commit_msg: "Übertrage Nachricht" + review: "Prüfen" version_history: "Versionshistorie" version_history_for: "Versionsgeschichte für: " + select_changes: "Wähle zwei Änderungen unten, um den Unterschied sehen zu können." + undo_prefix: "Rückgängig" + undo_shortcut: "(Strg+Z)" + redo_prefix: "Wiederholen" + redo_shortcut: "(Strg+Umschalt+Z)" + play_preview: "Spiele eine Vorschau des momentanen Levels." result: "Ergebnis" results: "Ergebnisse" description: "Beschreibung" @@ -173,7 +192,10 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: medium: "Mittel" hard: "Schwer" player: "Spieler" - player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Stufe" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Krieger" + ranger: "Waldläufer" + wizard: "Magier" units: second: "Sekunde" @@ -208,44 +230,45 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: failing: "Fehlgeschlagen" action_timeline: "Aktionszeitstrahl" click_to_select: "Klicke auf eine Einheit, um sie auszuwählen." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" + control_bar_multiplayer: "Mehrspieler" + control_bar_join_game: "Spiel beitreten" reload: "Neu laden" reload_title: "Gesamten Code neu laden?" reload_really: "Bist Du sicher, dass Du das Level neu beginnen willst?" reload_confirm: "Alles neu laden" + victory: "Triumph" victory_title_prefix: "" victory_title_suffix: " Abgeschlossen" - victory_sign_up: "Melde Dich an, um Fortschritte zu speichern" + victory_sign_up: "Melde Dich an, um Fortschritte zu speichern." victory_sign_up_poke: "Möchtest Du Neuigkeiten per Mail erhalten? Erstelle einen kostenlosen Account und wir halten Dich auf dem Laufenden." victory_rate_the_level: "Bewerte das Level: " # Only in old-style levels. victory_return_to_ladder: "Zurück zur Rangliste" victory_play_continue: "Fortsetzen" - victory_play_skip: "Überspringen" - victory_play_next_level: "Spiel das nächste Level" - victory_play_more_practice: "Mehr Training" - victory_play_too_easy: "Zu einfach" - victory_play_just_right: "Genau richtig" - victory_play_too_hard: "Zu schwer" victory_saving_progress: "Fortschritt speichern" victory_go_home: "Geh auf die Startseite" # Only in old-style levels. victory_review: "Erzähl uns davon!" # Only in old-style levels. victory_hour_of_code_done: "Bist Du fertig?" victory_hour_of_code_done_yes: "Ja, ich bin mit meiner Code-Stunde fertig!" + victory_experience_gained: "Gewonnene XP" + victory_gems_gained: "Gewonnene Edelsteine" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Anleitung" tome_minion_spells: "Die Zaubersprüche Deiner Knechte" # Only in old-style levels. - tome_read_only_spells: "Nur-lesen Zauberspüche" # Only in old-style levels. + tome_read_only_spells: "Nur-lesen Zaubersprüche" # Only in old-style levels. tome_other_units: "Andere Einheiten" # Only in old-style levels. - tome_cast_button_run: "Run" - tome_cast_button_running: "Running" - tome_cast_button_ran: "Ran" + tome_cast_button_run: "Zaubern" + tome_cast_button_running: "Wird gezaubert" + tome_cast_button_ran: "Wurde gezaubert" tome_submit_button: "Senden" tome_reload_method: "Original Code für diese Methode neu laden" # Title text for individual method reload button. tome_select_method: "Methode auswählen" - tome_see_all_methods: "Alle bearbeitbare Methoden anzeigen" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "Alle bearbeitbaren Methoden anzeigen" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Wähle jemanden aus, um " tome_available_spells: "Verfügbare Zauber" tome_your_skills: "Deine Fähigkeiten" + tome_help: "Hilfe" tome_current_method: "Aktuelle Methode" hud_continue_short: "Fortsetzen" code_saved: "Code gespeichert" @@ -254,61 +277,103 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: loading_ready: "Bereit!" loading_start: "Starte Level" problem_alert_title: "Repariere deinen Code" + problem_alert_help: "Hilfe" time_current: "Aktuell" time_total: "Total" time_goto: "Gehe zu" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Erneut versuchen" infinite_loop_reset_level: "Level zurücksetzen" infinite_loop_comment_out: "Meinen Code auskommentieren" tip_toggle_play: "Wechsel zwischen Play und Pause mit Strg+P." - tip_scrub_shortcut: "Spule vor und zurück mit Strg+[ und Strg+]" - tip_guide_exists: "Klicke auf die Anleitung am oberen Ende der Seite für nützliche Informationen" + tip_scrub_shortcut: "Spule vor und zurück mit Strg+[ und Strg+]" # {change} + tip_guide_exists: "Klicke auf die Anleitung am oberen Ende der Seite für nützliche Informationen." tip_open_source: "CodeCombat ist 100% quelloffen!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat startete seine Beta im Oktober 2013." tip_think_solution: "Denke über die Lösung nach, nicht über das Problem." tip_theory_practice: "In der Theorie gibt es keinen Unterschied zwischen Theorie und Praxis. In der Praxis schon. - Yogi Berra" - tip_error_free: "Es gibt zwei Wege fehlerfreie Programme zu schreiben; nur der Dritte funktioniert. - Alan Perlis" + tip_error_free: "Es gibt zwei Wege fehlerfreie Programme zu schreiben. Nur der Dritte funktioniert. - Alan Perlis" tip_debugging_program: "Wenn Debugging der Prozess zum Fehler entfernen ist, dann muss Programmieren der Prozess sein Fehler zu machen. - Edsger W. Dijkstra" - tip_forums: "Gehe zum Forum und sage uns was du denkst!" + tip_forums: "Komm ins Forum und sage uns was du denkst!" tip_baby_coders: "In der Zukunft werden sogar Babies Erzmagier sein." tip_morale_improves: "Das Laden wird weiter gehen bis die Stimmung sich verbessert." tip_all_species: "Wir glauben an gleiche Chancen für alle Arten Programmieren zu lernen." - tip_reticulating: "Spines neuberechnen." + tip_reticulating: "Spines neu berechnen." tip_harry: "Du bist ein Zauberer, " tip_great_responsibility: "Mit großen Programmierfähigkeiten kommt große Verantwortung." tip_munchkin: "Wenn du dein Gemüse nicht isst, besucht dich ein Zwerg während du schläfst." - tip_binary: "Es gibt auf der Welt nur 10 Arten von Menschen: die, welche Binär verstehen und die, welche nicht." - tip_commitment_yoda: "Ein Programmier muss die größte Hingabe haben, den ernstesten Verstand. ~ Yoda" - tip_no_try: "Tu. Oder tu nicht. Es gibt kein Versuchen. - Yoda" - tip_patience: "Geduld du musst haben, junger Padawan. - Yoda" - tip_documented_bug: "Ein dokumentierter Fehler ist kein Fehler; er ist ein Merkmal." - tip_impossible: "Es wirkt immer unmöglich bis es vollbracht ist. - Nelson Mandela" + tip_binary: "Es gibt auf der Welt nur 10 Arten von Menschen: Die, die Binär verstehen und die, die es nicht tun." + tip_commitment_yoda: "Ein Programmierer muss die größte Hingabe haben, den ernstesten Verstand. - Yoda" + tip_no_try: "Tun oder nicht tun. Es gibt kein Versuchen. - Yoda" + tip_patience: "Geduld du haben musst, junger Padawan. - Yoda" + tip_documented_bug: "Ein dokumentierter Fehler ist kein Fehler: Er ist ein Besonderheit." + tip_impossible: "Es wirkt immer unmöglich, bis es vollbracht ist. - Nelson Mandela" tip_talk_is_cheap: "Reden ist billig. Zeig mir den Code. - Linus Torvalds" - tip_first_language: "Das schwierigste, das du jemals lernen wirst, ist die erste Programmiersprache. - Alan Kay" + tip_first_language: "Das Schwierigste, das du jemals lernen wirst, ist die erste Programmiersprache. - Alan Kay" tip_hardware_problem: "Q: Wie viele Programmierer braucht man um eine Glühbirne auszuwechseln? A: Keine, es ist ein Hardware-Problem." tip_hofstadters_law: "Hofstadter's Gesetz: Es dauert immer länger als erwartet, auch wenn du Hofstadter's Gesetz anwendest." - tip_premature_optimization: "Vorzeitige Optimierung ist die Wurzel alles Übels (oder der mindestens Meister) bei der Programmierung - Donald Knuth" + tip_premature_optimization: "Vorzeitige Optimierung ist die Wurzel allen Übels (oder mindestens des meisten) bei der Programmierung. - Donald Knuth" tip_brute_force: "Verwende im Zweifelsfall rohe Gewalt. - Ken Thompson" - customize_wizard: "Bearbeite den Zauberer" + tip_extrapolation: "Es gibt nur zwei Sorten Menschen, diejenigen die aus unvollständigen Informationen Schlüsse ziehen können, ..." + tip_superpower: "Programmieren ist das nächste an einer Superkraft was wir haben." + tip_control_destiny: "In wirklichem Open Source hat man das Recht, sein Schicksal selbst zu bestimmen. - Linus Torvalds" + tip_no_code: "Kein Code ist schneller als kein Code." + tip_code_never_lies: "Der Code lügt nie, Kommentare manchmal schon. - Ron Jeffries" + tip_reusable_software: "Bevor ein Programm wiederverwendbar ist, muss es erst mal verwendbar sein." + tip_optimization_operator: "Jede Sprache hat einen Optimierungs-Operator. In den meisten Sprachen lautet der Operator ‘//’" + tip_lines_of_code: "Ein Programm an den Zeilen seines Quellcodes zu messen entspricht dem Messen des Fortschritts eines Flugzeugentwurfsprozesses nach Gewicht. - Bill Gates" + tip_source_code: "Ich will die Welt verändern, aber sie werden mir den Quellcode nicht geben" +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." + tip_google: "Hast du ein Problem, das du nicht lösen kannst? Google es!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" + tip_open_source_contribute: "Du kannst dabei helfen, CodeCombat zu verbessern." +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventar" - save_load_tab: "Speichere/Lade" + save_load_tab: "Speicher/Lade" options_tab: "Einstellungen" - guide_tab: "Guide" + guide_tab: "Handbuch" + guide_video_tutorial: "Video Anleitung" + guide_tips: "Hinweise" multiplayer_tab: "Mehrspieler" auth_tab: "Registrieren" inventory_caption: "Rüste deinen Helden aus" choose_hero_caption: "Wähle Helden, Sprache" save_load_caption: "... und schaue dir die Historie an" options_caption: "konfiguriere Einstellungen" - guide_caption: "Doku und Tipps" + guide_caption: "Handbuch und Tipps" multiplayer_caption: "Spiele mit Freunden!" auth_caption: "Fortschritt speichern." + leaderboard: + leaderboard: "Bestenliste" + view_other_solutions: "Andere Lösungen" # {change} + scores: "Punktzahl" + top_players: "Die besten Spieler von" + day: "Heute" + week: "dieser Woche" + all: "insgesamt" + time: "Zeit" + damage_taken: "Erhaltener Schaden" + damage_dealt: "Ausgeteilter Schaden" + difficulty: "Schwierigkeit" + gold_collected: "Gold gesammelt" + inventory: choose_inventory: "Gegenstände ausrüsten" equipped_item: "Hinzugefügt" + required_purchase_title: "Benötigt" available_item: "Verfügbar" restricted_title: "Eingeschränkt" should_equip: "(Doppelklick zum Hinzufügen)" @@ -316,15 +381,89 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: locked: "(gesperrt)" restricted: "(benötigt für dieses Level)" equip: "Ausrüsten" - unequip: "Abrüsten" + unequip: "Ablegen" buy_gems: few_gems: "Ein paar Edelsteine" - pile_gems: "Stapel von Edelsteinen" - chest_gems: "Kiste von Edelsteinen" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + pile_gems: "Ein Stapel Edelsteine" + chest_gems: "Eine Kiste voller Edelsteine" + purchasing: "Kaufabwicklung..." + declined: "Deine Karte wurde abgelehnt" + retrying: "Serverfehler, versuche es erneut." + prompt_title: "Nicht genug Edelsteine" + prompt_body: "Benötigst du mehr?" + prompt_button: "Laden betreten" + recovered: "Vorangegangener Edelsteinkauf rückgängig gemacht. Aktualisiere bitte die Seite." + price: "x3500 / Monat" + + subscribe: + comparison_blurb: "Verbessere deine Fähigkeiten mit einem CodeCombat Abonnement" + feature1: "60+ Basislevel in 4 Gebieten" # {change} + feature2: "7 mächtige <strong>neue Helden</strong> mit einzigartigen Fertigkeiten" # {change} + feature3: "30+ Bonuslevel" # {change} + feature4: "<strong>3500 Bonusedelsteine</strong> jeden Monat!" + feature5: "Videoanleitungen" + feature6: "Premium Emailsupport" +# feature7: "Private <strong>Clans</strong>" + free: "Kostenlos" + month: "Monat" + subscribe_title: "Abonnieren" + unsubscribe: "Abmelden" + confirm_unsubscribe: "Abmeldung bestätigen" + never_mind: "Keine Sorge. Ich hab dich trotzdem lieb." + thank_you_months_prefix: "Danke für deine Unterstützung in den letzten" + thank_you_months_suffix: "Monaten." + thank_you: "Danke, dass du CodeCombat unterstützt." + sorry_to_see_you_go: "Schade, dass du gehst! Bitte teile uns mit, was wir hätten besser machen können." + unsubscribe_feedback_placeholder: "Oh, was haben wir getan?" + parent_button: "Frag deine Eltern" + parent_email_description: "Wir werden ihnen eine Email senden, damit sie dir ein CodeCombat Abo kaufen können." + parent_email_input_invalid: "Emailadresse nicht gültig" + parent_email_input_label: "Emailadresse der Eltern" + parent_email_input_placeholder: "Emailadresse der Eltern eingeben" + parent_email_send: "Sende Email" + parent_email_sent: "Email gesendet!" + parent_email_title: "Wie lautet die Emailadresse deiner Eltern?" + parents: "Für Eltern" + parents_title: "Dein Kind lernt zu programmieren." # {change} + parents_blurb1: "Mit CodeCombat, lernt dein Kind richtige Programme zu schreiben. Es fängt mit einfachen Befehlen an, und schreitet ganz unmerklich zu schwierigeren Themen fort." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "Für 9.99 im Monat, bekommt es jede Woche neue Herausforderungen sowie persönlichen Email Support von professionellen Programmierern." # {change} + parents_blurb3: "Kein Risiko: 100% Geld zurück Garantie, und 1-Klick Abokündigung." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "Monatsabo" + subscription_required_to_play: "Leider musst du ein Abo haben, um dieses Level spielen zu können." + unlock_help_videos: "Abonniere, um alle Videoanleitungen freizuschalten." + personal_sub: "Persönliches Abonnement" # Accounts Subscription View below + loading_info: "Lade Abonnementinformationen..." + managed_by: "Verwaltet durch" + will_be_cancelled: "Läuft ab am" + currently_free: "Du hast aktuell ein kostenloses Abonnement" + currently_free_until: "Du hast aktuell ein kostenloses Abonnement bis zum" + was_free_until: "Du hattest ein kostenloses Abonnement bis zum" + managed_subs: "Verwaltete Abonnements" + managed_subs_desc: "Abonnements für andere Spieler (Studenten, Kinder, usw.) hinzufügen" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." + group_discounts: "Gruppenrabatt" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." + group_discounts_1st: "Erstes Abonnement (inklusive deinem)" # {change} + group_discounts_full: "voller Preis" + group_discounts_2nd: "Abonnements 2-11" + group_discounts_20: "20% Rabatt" + group_discounts_12th: "Abonnement 12+" + group_discounts_40: "40% Rabatt" + subscribing: "Abonniere..." + recipient_emails_placeholder: "Gib die E-Mail-Adressen ein, für die du ein Abonnement übernehmen möchtest, eine pro Zeile." + subscribe_users: "Abonnement für Spieler übernehmen" + users_subscribed: "Abonnement für Spieler übernommen:" + no_users_subscribed: "Abonnement für keine Spieler übernommen, bitte prüfe deine E-Mail-Adressen." + current_recipients: "Aktuelle Empfänger" + unsubscribing: "Abmelden..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: choose_hero: "Wähle deinen Helden" @@ -333,41 +472,54 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: default: "Standard" experimental: "Experimentell" python_blurb: "Einfach jedoch leistungsfähig, Python ist eine gute Allzweck-Programmiersprache." - javascript_blurb: "Die Sprache des Web." + javascript_blurb: "Die Sprache des Netzes." coffeescript_blurb: "Schönere JavaScript Syntax." clojure_blurb: "Ein modernes Lisp." lua_blurb: "Skriptsprache für Spiele." io_blurb: "Simpel aber obskur." status: "Status" + hero_type: "Typ" weapons: "Waffen" - weapons_warrior: "Schwert - Kurze Reichweite, Kein Zauber" - weapons_ranger: "Armbrust, Geschütz - Hohe Reichweite, Kein Zauber" + weapons_warrior: "Schwert - Kurze Reichweite, Keine Zauber" + weapons_ranger: "Armbrust, Geschütz - Hohe Reichweite, Keine Zauber" weapons_wizard: "Stäbe, Stäbe - Lange Reichweite, Zauber" attack: "Schaden" # Can also translate as "Attack" health: "Gesundheit" speed: "Geschwindigkeit" regeneration: "Regeneration" range: "Reichweite" # As in "attack or visual range" - blocks: "Blockieren" # As in "this shield blocks this much damage" + blocks: "Blocken" # As in "this shield blocks this much damage" + backstab: "Meucheln" # As in "this dagger does this much backstab damage" skills: "Fähigkeiten" + attack_1: "Teilt aus" + attack_2: "des genannten" + attack_3: "Waffenschadens." + health_1: "Erhält" + health_2: "der genannten" + health_3: "Rüstungspunkte." + speed_1: "Gehe zu" + speed_2: "Meter pro Sekunde." + available_for_purchase: "Zum Kauf verfügbar" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Level zum Freischalten:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Nur bestimmte Helden können dieses Level spielen." -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + skill_docs: + writable: "beschreibbar" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "schreibgeschützt" + action_name: "Name" + action_cooldown: "Benötigt" + action_specific_cooldown: "Cooldown" + action_damage: "Schaden" + action_range: "Reichweite" + action_radius: "Radius" + action_duration: "Dauer" + example: "Beispiel" + ex: "z.B." # Abbreviation of "example" + current_value: "Aktueller Wert" + default_value: "Standardwert" + parameters: "Parameter" + returns: "Gibt zurück" + granted_by: "Gewährt durch" save_load: granularity_saved_games: "Gespeichert" @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: volume_label: "Lautstärke" music_label: "Musik" music_description: "Schalte Hintergrundmusik an/aus." - autorun_label: "Autorun" - autorun_description: "Steuere automatische Programmausführung." editor_config: "Editor Einstellungen" editor_config_title: "Editor Einstellungen" editor_config_level_language_label: "Sprache für dieses Level" @@ -393,56 +543,150 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: editor_config_livecompletion_description: "Zeigt Vorschläge der Auto-Vervollständigung an während du tippst." editor_config_invisibles_label: "Zeige unsichtbare Zeichen" editor_config_invisibles_description: "Zeigt unsichtbare Zeichen wie Leertasten an." - editor_config_indentguides_label: "Zeige Einrückungshilfe" - editor_config_indentguides_description: "Zeigt vertikale Linien an um Einrückungen besser zu sehen." + editor_config_indentguides_label: "Zeige Einrückhilfe" + editor_config_indentguides_description: "Zeigt vertikale Linien an, um Einrückungen besser zu sehen." editor_config_behaviors_label: "Intelligentes Verhalten" editor_config_behaviors_description: "Vervollständigt automatisch Klammern und Anführungszeichen." about: why_codecombat: "Warum CodeCombat?" - why_paragraph_1: "Programmieren lernen? Du brauchst keine Stunden. Du musst einen Haufen Code schreiben und dabei Spaß haben." + why_paragraph_1: "Programmieren lernen? Du brauchst keine Unterrichtsstunden. Du musst einen Haufen Code schreiben und dabei Spaß haben." why_paragraph_2_prefix: "Darum geht's beim Programmieren. Es soll Spaß machen. Nicht so einen Spaß wie" why_paragraph_2_italic: "jau, 'ne Plakette" why_paragraph_2_center: "sondern Spaß wie" - why_paragraph_2_italic_caps: "NEIN MUTTI ICH MUSS NOCH DEN LEVEL BEENDEN !" - why_paragraph_2_suffix: "Deshalb ist CodeCombat ein Multiplayerspiel und kein spielähnlicher Kurs. Wir werden nicht aufhören bis du nicht mehr aufhören kannst -- nur diesmal ist das eine gute Sache." - why_paragraph_3: "Wenn dich Spiele süchtig machen, dass lass dich von diesem süchtig machen und werde ein Zauberer des Technologiezeitalters." + why_paragraph_2_italic_caps: "NEIN MUTTI, ICH MUSS NOCH DAS LEVEL BEENDEN !" + why_paragraph_2_suffix: "Deshalb ist CodeCombat ein Multiplayerspiel und kein spielähnlicher Kurs. Wir werden nicht aufhören, bis du nicht mehr aufhören kannst -- nur diesmal ist das eine gute Sache." + why_paragraph_3: "Wenn dich Spiele süchtig machen, dann lass dich von diesem süchtig machen und werde ein Zauberer des Technologiezeitalters." press_title: "Blogger/Presse" press_paragraph_1_prefix: "Sie möchten über uns schreiben? Laden und benutzen Sie ruhig alle Ressourcen in unserem" press_paragraph_1_link: "Presse-Paket" press_paragraph_1_suffix: ". Alle Logos und Bilder können ohne unsere vorherige Zustimmung verwendet werden." team: "Team" george_title: "CEO" - george_blurb: "Businesser" + george_blurb: "Vertriebler" scott_title: "Programmierer" scott_blurb: "Der Vernünftige" nick_title: "Programmierer" nick_blurb: "Motivationsguru" michael_title: "Programmierer" michael_blurb: "Sys Admin" - matt_title: "Programmierer" + matt_title: "Programmierer" # {change} matt_blurb: "Radfahrer" + cat_title: "Chief Artisan" + cat_blurb: "Luftbändiger" + josh_title: "Spiel Designer" + josh_blurb: "Boden ist Lava" + jose_title: "Musik" + jose_blurb: "Taking Off" + retrostyle_title: "Illustration" + retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat für Lehrer" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." + sys_requirements_title: "System Voraussetzungen" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." + sys_requirements_2: "Nutzen Sie die neuesten Versionen von Google Chrome oder Firefox." # {change} + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Neue Version speichern" new_major_version: "Neue Hauptversion" + submitting_patch: "Übermittele Patch..." cla_prefix: "Damit Änderungen gespeichert werden können, musst du unsere Lizenzbedingungen (" cla_url: "CLA" cla_suffix: ") akzeptieren." cla_agree: "Ich stimme zu" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Kontaktiere CodeCombat" welcome: "Schön von Dir zu hören! Benutze dieses Formular um uns eine Email zu schicken." - contribute_prefix: "Wenn Du Interesse hast, uns zu unterstützen dann sieh dir die " - contribute_page: "Unterstützer Seite" - contribute_suffix: " an!" forum_prefix: "Für alle öffentlichen Themen, benutze stattdessen " forum_page: "unser Forum" forum_suffix: "." + faq_prefix: "Es gibt auch ein" + faq: "FAQ" + subscribe_prefix: "Wenn du Hilfe brauchst ein Level zu lösen, bitte" + subscribe: "kaufe ein CodeCombat Abonnement" + subscribe_suffix: "und wir werden dir gerne bei deinem Code helfen." + subscriber_support: "Da du ein CodeCombat Abonnent bist, bekommt deine E-Mail Priorität." + screenshot_included: "Bildschirmfoto hinzugefügt." + where_reply: "Wohin sollen wir antworten?" send: "Sende Feedback" contact_candidate: "Kontaktiere Kandidaten" # Deprecated - recruitment_reminder: "Benutzen Sie dieses Formular um Kontakt zu Kandidaten aufzunehmen, an denen Sie interessiert sind. Bedenken Sie das CodeCombat 15% des ersten Jahresgehaltes berechnet. Diese Gebühr wird fällig wenn Sie den Kandidaten einstellen und ist für 90 Tage rückerstattungsfähig, sollte der Mitarbeiter nicht eingestellt bleiben. Mitarbeiter die für Teilzeit, Remote oder eine Auftragsarbeit eingestellt werden sind kostenlos, das gilt auch für Praktikanten." # Deprecated + recruitment_reminder: "Benutzen Sie dieses Formular, um Kontakt zu Kandidaten aufzunehmen, an denen Sie interessiert sind. Bedenken Sie, dass CodeCombat 15% des ersten Jahresgehaltes berechnet. Diese Gebühr wird fällig, wenn Sie den Kandidaten einstellen und ist für 90 Tage rückerstattungsfähig, sollte der Mitarbeiter nicht eingestellt bleiben. Mitarbeiter, die für Teilzeit, Remote oder eine Auftragsarbeit eingestellt werden, sind kostenlos; das gilt auch für Praktikanten." # Deprecated account_settings: title: "Accounteinstellungen" @@ -450,20 +694,27 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: autosave: "Sichere Änderungen automatisch" me_tab: "Ich" picture_tab: "Bild" + delete_account_tab: "Account löschen" + wrong_email: "Falsche Email Adresse" +# wrong_password: "Wrong Password" upload_picture: "Ein Bild hochladen" + delete_this_account: "Das Löschen deines Accounts kann nicht rückgängig gemacht werden!" + god_mode: "Gottmodus" password_tab: "Passwort" emails_tab: "Emails" admin: "Admin" new_password: "Neues Passwort" new_password_verify: "Passwort verifizieren" + type_in_email: "Email eingeben, um Löschung zu bestätigen" # {change} +# type_in_password: "Also, type in your password." email_subscriptions: "Email Abonnements" email_subscriptions_none: "Keine Email Abonnements." email_announcements: "Ankündigungen" email_announcements_description: "Erhalte regelmäßig Ankündigungen zu deinem Account." email_notifications: "Benachrichtigungen" email_notifications_summary: "Steuerung für personalisierte, automatische Emailbenachrichtigungen im Zusammenhang mit deiner CodeCombat Aktivität." - email_any_notes: "Jegliche Benachrichtungen" - email_any_notes_description: "Deaktivieren um alle aktiven Benachrichtigungen zu stoppen." + email_any_notes: "Jegliche Benachrichtigungen" + email_any_notes_description: "Deaktivieren, um alle aktiven Benachrichtigungen zu stoppen." email_news: "News" email_recruit_notes: "Job-Angebote" email_recruit_notes_description: "Wenn du besonders gut spielst, werden wir dich evtl. kontaktieren um dir einen (besseren) Job zu suchen." @@ -481,18 +732,17 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: job_profile_explanation: "Hi! Fülle dies aus und wir melden uns bei dir bezüglich des Auffindens eines Jobs als Programmierer" sample_profile: "Ein Beispielprofil ansehen" view_profile: "Dein Profil ansehen" - wizard_tab: "Zauberer" - wizard_color: "Die Farbe der Kleidung des Zauberers" keyboard_shortcuts: keyboard_shortcuts: "Tastaturkürzel" space: "Leertaste" enter: "Eingabetaste" +# press_enter: "press enter" escape: "Escape" shift: "Umschalttaste" run_code: "Starte aktuellen Code." run_real_time: "Führe in Echtzeit aus." - continue_script: "Setze nach aktuellenm Skript fort." + continue_script: "Setze nach aktuellem Skript fort." skip_scripts: "Überspringe alle überspringbaren Skripte." toggle_playback: "Umschalten Play/Pause." scrub_playback: "Scrubbe vor und zurück durch die Zeit." @@ -503,17 +753,16 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: toggle_pathfinding: "Wegfindungs-Overlay an/aus." beautify: "Verschönere deinen Code durch die Standardisierung der Formatierung." maximize_editor: "Maximiere/Minimiere Code Editor." - move_wizard: "Bewege deinen Zauberer durch das Level." community: main_title: "CodeCombat Community" - introduction: "Schaue dir unten die Möglichkeiten wie du mitwirken kannst und entscheide was dir am meisten Spass macht. Wir freuen uns auf die Zusammenarbeit mit dir!" + introduction: "Schaue dir unten die Möglichkeiten wie du mitwirken kannst an und entscheide, was dir am meisten Spaß macht. Wir freuen uns auf die Zusammenarbeit mit dir!" level_editor_prefix: "Benutze den CodeCombat" - level_editor_suffix: "um Level zu erstellen oder zu bearbeiten. Benutzer haben bereits Level für ihre Klassen, Freunde, Hackathons, Schüler und Geschwister erstellt. Wenn das Neuerstellen eines Levels abschreckend wirkt, dann kannst du erstmal ein bestehendes kopieren!" + level_editor_suffix: "um Level zu erstellen oder zu bearbeiten. Benutzer haben bereits Level für ihre Klassen, Freunde, Hackathons, Schüler und Geschwister erstellt. Wenn das Neuerstellen eines Levels abschreckend wirkt, dann kannst du erst mal ein bestehendes kopieren!" thang_editor_prefix: "Wir nennen Einheiten innerhalb des Spiels 'Thangs'. Benutze den" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." + thang_editor_suffix: "um den CodeCombat Grafikquelltext zu ändern. Erlaube Einheiten Geschosse zu werfen, ändere die Richtung einer Animation, ändere die Trefferpunkte einer Einheit, oder lade deine eigenen Vektorsprite hoch." article_editor_prefix: "Hast du einen Fehler in unseren Dokus gefunden? Willst du Anleitungen für deine Kreationen erstellen? Schau dir den" - article_editor_suffix: "und hilf CodeCombat Spielern das meiste aus ihrer Spielzeit heraus zu bekommen." + article_editor_suffix: "und hilf CodeCombat Spielern, das meiste aus ihrer Spielzeit herauszuholen." find_us: "Finde uns auf diesen Seiten" social_blog: "Lese den CodeCombat Blog auf Sett" social_discource: "Schließe dich den Diskussionen in unserem Discourse Forum an" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: social_hipchat: "Chatte mit uns in unserem öffentlichen CodeCombat HipChat Raum" contribute_to_the_project: "Trage zu diesem Projekt bei" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "Erzmagier" archmage_title_description: "(Programmierer)" + archmage_summary: "Wenn du ein Entwickler bist, der daran interessiert ist Lernspiele zu programmieren, werde ein Erzmagier, um uns zu helfen, CodeCombat zu erschaffen!" artisan_title: "Handwerker" artisan_title_description: "(Level Entwickler)" + artisan_summary: "Erschaffe und teile Level zum spielen für dich und deine Freunde. Werde ein Handwerker, um die Kunst zu erlernen, anderen Programmieren zu lehren." adventurer_title: "Abenteurer" adventurer_title_description: "(Level Spieltester)" + adventurer_summary: "Bekomme unsere neuen Level (sogar unseren Abonnement Inhalt) kostenlos eine Woche früher und hilf uns, Fehler vor der Veröffentlichung zu finden." scribe_title: "Schreiber" scribe_title_description: "(Artikel Editor)" + scribe_summary: "Guter Code braucht gute Dokumentation. Schreibe, bearbeite und verbessere die, von weltweit Millionen von Spielern, gelesenen Dokumentationen." diplomat_title: "Diplomat" diplomat_title_description: "(Übersetzer)" + diplomat_summary: "CodeCombat wird in 45+ Sprachen von unseren Diplomaten übersetzt. Hilf uns und steuere Übersetzungen bei." ambassador_title: "Botschafter" ambassador_title_description: "(Support)" + ambassador_summary: "Zähme unsere Forum Benutzer und weise jenen mit Fragen die Richtung. Unsere Botschafter repräsentieren CodeCombat vor der Welt." editor: main_title: "CodeCombat Editoren" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: thang_title: "Thang Editor" level_title: "Level Editor" achievement_title: "Achievement Editor" + poll_title: "Umfrage Editor" back: "Zurück" revert: "Zurücksetzen" - revert_models: "Models zurücksetzen." + revert_models: "Modelle zurücksetzen." pick_a_terrain: "Wähle ein Terrain" + dungeon: "Dungeon" + indoor: "Indoor" + desert: "Wüste" + grassy: "Gräsern" +# mountain: "Mountain" +# glacier: "Glacier" small: "Klein" - grassy: "Grasig" + large: "Groß" fork_title: "Forke neue Version" fork_creating: "Erzeuge Fork..." generate_terrain: "Generiere Terrain" more: "Mehr" wiki: "Wiki" live_chat: "Live Chat" + thang_main: "Main" + thang_spritesheets: "Sprite Palette" + thang_colors: "Farben" level_some_options: "Einige Einstellungsmöglichkeiten?" level_tab_thangs: "Thangs" level_tab_scripts: "Skripte" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: level_tab_thangs_all: "Alle" level_tab_thangs_conditions: "Startbedingungen" level_tab_thangs_add: "Thangs hinzufügen" +# level_tab_thangs_search: "Search thangs" + add_components: "Kommentar hinzufügen" + component_configs: "Komponenten Konfiguration" + config_thang: "Doppelklicke, um Thang zu konfigurieren" delete: "Löschen" duplicate: "Duplizieren" + stop_duplicate: "Stop Duplizieren" rotate: "Drehen" level_settings_title: "Einstellungen" level_component_tab_title: "Aktuelle Komponenten" @@ -591,104 +905,94 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: new_thang_title_login: "Melde dich an um einen neuen Thang-Typen zu erstellen" new_level_title_login: "Melde dich an um ein neues Level zu erstellen" new_achievement_title: "Erstelle ein neues Achievement" - new_achievement_title_login: "Melde dich an um ein neues Achievement zu erstellen" + new_achievement_title_login: "Melde dich an um einen neuen Erfolg zu erstellen" + new_poll_title: "Erstelle eine neue Umfrage" + new_poll_title_login: "Melde dich an um eine neue Umfrage zu erstellen" article_search_title: "Durchsuche Artikel hier" thang_search_title: "Durchsuche Thang-Typen hier" level_search_title: "Durchsuche Levels hier" - achievement_search_title: "Durchsuche Achievements" + achievement_search_title: "Durchsuche Erfolge" + poll_search_title: "Durchsuche Umfragen" read_only_warning2: "Warnung: Du kannst hier keine Änderungen speichern, weil du nicht angemeldet bist." - no_achievements: "Es wurden noch keine Achievements zu diesem Level hinzugefügt." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" -# pop_i18n: "Populate I18N" + no_achievements: "Es wurden noch keine Erfolge zu diesem Level hinzugefügt." + achievement_query_misc: "Sonstige Schlüsselerfolge" + achievement_query_goals: "Level Erfolge" + level_completion: "abgeschlossene Level" + pop_i18n: "Bevölkere I18N" + tasks: "Aufgaben" + clear_storage: "Lösche Deine lokalen Änderungen" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Vorschau" edit_article_title: "Artikel bearbeiten" + polls: + priority: "Priorität" + contribute: page_title: "Mitwirken" - character_classes_title: "Charakter Klassen" - introduction_desc_intro: "Wir haben hohe Erwartungen für CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " - introduction_desc_github_url: "CodeCombat ist komplett OpenSource" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." - introduction_desc_ending: "Wir hoffen du nimmst an unserer Party teil!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" + intro_blurb: "CodeCombat ist zu 100% Open Source! Hunderte hingebungsvolle Spieler haben uns geholfen das Spiel zu dem zu machen was es heute ist. Tritt uns bei und schreibe das nächste Kapitel in CodeCombat' Aufgabe, der Welt das Programmieren zu lehren!" alert_account_message_intro: "Hey du!" alert_account_message: "Um Klassen-Emails abonnieren zu können, musst du dich zuerst anmelden." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." - class_attributes: "Klassenattribute" - archmage_attribute_1_pref: "Kentnisse in " -# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." -# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" + archmage_introduction: "Einer der größten Vorteile daran ein Spiel aufzubauen, ist es, dass so viele verschiedene Aspekte eine Rolle spielen. Grafiken, Sound, Echtzeit Networking, Social Networking und natürlich viele der gewöhnlichen Aspekte des Programmierens, von low-level Datenbankmanagement und Server Administration bis hin zum Aufbau von Design und Interface. Es gibt viel zu tun und wenn du ein erfahrener Programmierer bist, mit einer Veranlagung dazu, wirklich knallhart bei CodeCombat einzutauchen, dann könnte diese Klasse etwas für dich sein. Wir würden uns wahnsinnig über deine Hilfe dabei freuen, das beste Programmierspiel der Welt aufzubauen." + class_attributes: "Eigenschaften" + archmage_attribute_1_pref: "Kenntnisse in " + archmage_attribute_1_suf: ", oder der Drang danach zu lernen. Das meiste unseres Codes ist in dieser Sprache geschrieben. Wenn du ein Fan von Ruby oder Python bist, dann wirst du dich pudelwohl fühlen. Es ist wie JavaScript, nur mit einer angenehmeren Syntax." + archmage_attribute_2: "Einige Erfahrung in Programmieren und Eigeninitiative. Wir werden dir helfen dich zu orientieren, aber wir haben nicht Zeit, dich zu trainieren." + how_to_join: "Wie du mitmachen kannst" join_desc_1: "Jeder kann mithelfen! Schau dir unseren " join_desc_2: "um anzufangen, und hake die Checkbox unten an um dich als mutiger Erzmagier einzutragen und über die neuesten Nachrichten per Email zu erhalten. Möchtest du dich darüber unterhalten was zu tun ist oder wie du dich besser beteiligen kannst? " join_desc_3: ", oder finde uns in unserem " join_desc_4: "und wir schauen von dort mal!" - join_url_email: "Emaile uns" + join_url_email: "Email uns" join_url_hipchat: "öffentlicher HipChat Raum" - more_about_archmage: "Erfahre mehr darüber wie du ein Erzmagier werden kannst" archmage_subscribe_desc: "Erhalte Emails über neue Programmier-Möglichkeiten und Ankündigungen." - artisan_summary_pref: "Du möchtest Levels erstellen und CodeCombats Arsenal erweitern? Unsere Nutzer spielen unseren Content schneller durch als wir ihn erstellen können! Momentan ist unser Level-Editor noch minimalistisch, also ist noch Vorsicht geboten. Die Levelerstellung wird noch etwas schwierig und buggy(fehlerbehaftet) sein. Wenn du Ideen für Kampagnen die for-loops umspannen" - artisan_summary_suf: ", dann ist diese Klasse für dich." -# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" -# artisan_introduction_suf: ", then this class might be for you." -# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" -# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." -# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" + artisan_introduction_pref: "Wir müssen neue Level erstellen. Problem: ihr wollt mehr und mehr Inhalte, aber unser Tag hat auch nur 24 Stunden. Und leider ist weder unsere Workstation die Beste, noch unser Leveleditor. Um es konkret zu sagen: selbst die Erschaffer des Level Editors können ihn gerade so benutzen, also Vorsicht. Wenn du aber Ideen für eine Kampagne hast, die von for-loops bis" + artisan_introduction_suf: ", dann ist diese Klasse vielleicht für dich." + artisan_attribute_1: "Erfahrung in der Erstellung von Inhalten (z.B. mit Blizzards level Editoren) ist von Vorteil, aber nicht Grundvoraussetzung!" + artisan_attribute_2: "Was ihr aber unbedingt benötigt ist der Wille wieder und wieder und wieder zu testen, auszuprobieren und zu ändern. Gute Level erstellt man, indem man sie selber testet, und vor allem anderen dabei zuschaut was sie anstellen. Um dann die dutzende von nötigen Fixes zu erstellen." + artisan_attribute_3: "Ausdauer!!! Unser Level Editor ist super vorläufig/alpha und frustrierend in der Nutzung. Du wurdest gewarnt!" artisan_join_desc: "Verwende den Level-Editor mit diesen Schritten, mehr oder weniger:" artisan_join_step1: "Lese die Dokumentation." artisan_join_step2: "Erstelle ein neues Level und erkunde existierende Level." artisan_join_step3: "Finde uns im öffentlichen HipChat Raum, falls du Hilfe brauchst." artisan_join_step4: "Poste deine Level im Forum um Feedback zu erhalten." - more_about_artisan: "Erfahre mehr darüber wie du ein Handwerker werden kannst" artisan_subscribe_desc: "Erhalte Emails über Level-Editor Updates und Ankündigungen." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" -# adventurer_forum_url: "our forum" -# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" - more_about_adventurer: "Erfahre mehr darüber wie du ein Abenteurer werden kannst" + adventurer_introduction: "Dein Job: du bist der Tank. Du wirst einstecken müssen, als gäbe es kein Morgen. Du wirst, fluchen, schwitzen und verzweifeln. Wir brauchen Leute, die unsere nagelneuen Level ausprobieren, und alle Bugs durchleiden. Spieldesign ist ein langer Prozeß, und niemand macht beim ersten Versuch alles richtig. Aber wenn du mithelfen willst, und aushalten kannst, dann sei dabei. Denn nur mit dieser deiner Klasse, gibt es beim zweiten, dritten, x-ten versuch ein besseres Level." + adventurer_attribute_1: "Ein Heißhunger nach Wissen. Du willst lernen wie man programmiert, und wir wollen es dir beibringen. Oder genauer, du willst es dir selber beibringen (und wir dir dabei helfen)." + adventurer_attribute_2: "Charismatisch. Sei rücksichtsvoll aber deutlich, wenn du erklärst was verbessert werden muss. Und gib Tipps wie dies umzusetzen ist." + adventurer_join_pref: "Schließe dich entweder mit einem Handwerker zusammen (oder rekrutiere einen!), oder klicke unten das Kästchen an, um Emails zu erhalten wenn wir neue Level zum testen haben. Wir werden dies auch über unsere Netzwerke veröffentlichen, wie z.B." + adventurer_forum_url: "unser Forum" + adventurer_join_suf: "wenn du also auf diese Weise informiert werden willst, melde dich hier an!" adventurer_subscribe_desc: "Erhalte Emails wenn es neue Levels zum Testen gibt." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " + scribe_introduction_pref: "CodeCombat soll mehr sein als eine Aneinanderreihung von Leveln. Es soll auch ein Tank des Programmierwissens sein, eine wiki der Programmierkonzepte mit der Level verknüpft werden können. Auf diese Weise müssen unsere Handwerker nicht jedesmal umständlich erklären, was ein Vergleichsoperator ist. Sondern sie können direkt auf detaillierte Artikel verlinken, die dies bereits ausführlich beschreiben. So ähnlich wie " scribe_introduction_url_mozilla: "Mozilla Developer Network" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." + scribe_introduction_suf: " erstellt hat. Wenn deine Idee von Spaß ist, das Konzept des Programmierens in Markdown zu Datei zu bringen, dann ist dies deine Klasse." + scribe_attribute_1: "Ein Händchen für Worte ist eigentlich alles was du brauchst. Nicht nur in Grammatik und Rechtschreibung, sondern die Fähigkeit, komplexe und komplizierte Ideen anderen zu vermitteln." contact_us_url: "Kontaktiere uns" - scribe_join_description: "erzähle uns ein bißchen über dich, deine Erfahrung mit der Programmierung und über welche Themen du schreiben möchtest. Wir werden von dort aus gehen!" - more_about_scribe: "Erfahre mehr darüber wie du ein Schreiber werden kannst" + scribe_join_description: "erzähle uns ein bisschen über dich, deine Erfahrung mit der Programmierung und über welche Themen du schreiben möchtest. Wir werden von dort aus gehen!" scribe_subscribe_desc: "Erhalte Emails über Ankündigungen zu schreibenden Artikeln." - diplomat_summary: "Es herrscht ein großes Interesse an CodeCombat in anderen Ländern die kein Englisch sprechen! Wir suchen nach Übersetzern die gewillt sind ihre Zeit mit der Übersetzung der Webseite zu verbringen, so dass CodeCombat so schnell wie möglich für alle weltweit zugänglich ist. Wenn du helfen möchtest CodeCombat International zugänglich zu machen, dann ist diese Klasse für dich." - diplomat_introduction_pref: "Also wenn es eines gibt was wir gelernt haben vom " + diplomat_introduction_pref: "Also wenn es eines gibt, was wir gelernt haben vom " diplomat_launch_url: "Launch im Oktober" - diplomat_introduction_suf: "ist das es ein großes Interesse an CodeCombat in anderen Ländern gibt! Wir stellen eine Truppe von Übersetzern zusammen, die gewillt sind einen Satz Wörten in einen anderen Satz Wörter umzuwandeln um CodeCombat der Welt so zugänglich wie möglich zu machen. Wenn du es magst eine Vorschau von zukünftigem Content zu erhalten und diese Level so schnell wie möglich deinen Landsleuten zur Verfügung zu stellen, dann ist diese Klasse vielleicht für dich." -# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." + diplomat_introduction_suf: "dann ist es, dass es ein großes Interesse an CodeCombat in anderen Ländern gibt! Wir stellen eine Truppe von Übersetzern zusammen, die gewillt sind ein Set Worte in ein anderes Set Worte umzuwandeln um CodeCombat der Welt so zugänglich wie möglich zu machen. Wenn du es magst eine Vorschau von zukünftigem Content zu erhalten und diese Level so schnell wie möglich deinen Landsleuten zur Verfügung zu stellen, dann ist diese Klasse vielleicht für dich." + diplomat_attribute_1: "Du sprichst/schreibst sowohl Englisch als auch die Sprache deiner Wahl flüssig. Wenn man komplizierte Informationen vermitteln will, muss man BEIDE Sprachen wirklich beherrschen." + diplomat_i18n_page_prefix: "Du kannst anfangen unsere Levels zu übersetzen, indem du auf unsere" + diplomat_i18n_page: "Übersetzungs-Seite" + diplomat_i18n_page_suffix: "gehst, oder unsere Schnittstelle und Webseite bei GitHub benutzt." diplomat_join_pref_github: "Finde deine Sprachdatei " diplomat_github_url: "bei GitHub" diplomat_join_suf_github: ", editiere sie online und reiche einen Pull Request ein. Außerdem, hake die Checkbox unten an um über neue Entwicklungen bei der Internationalisierung auf dem laufenden zu bleiben!" - more_about_diplomat: "Erfahre mehr darüber wie du ein Diplomat werden kannst" diplomat_subscribe_desc: "Erhalte Emails über i18n Entwicklungen und Level die übersetzt werden müssen." - ambassador_summary: "Wir versuchen eine Community aufzubauen und jede Community braucht ein Support-Team wenn es Probleme gibt. Wir haben Chats, Emails und soziale Netzwerke sodass unsere Benutzer mit dem Spiel vertraut werden können. Wenn du dabei helfen möchtest Leute zu animieren, Spass zu haben und programmieren zu lernen, dann ist diese Klasse für dich." - ambassador_introduction: "Wir bauen einen Community und du bist die Verbindung dazu. Wir haben Olark Chats, Email und soziale Netzwerke mit vielen Menschen mit denen man sprechen, dabei helfen mit dem Spiel vertraut zu werden und von lernen kann. Wenn du helfen möchtest Leute zu involvieren, Spass zu haben und ein gutes Gefühl für den Puls von CodeCombat und wo wir hn wollen, dann könnte diese Klasse für dich sein." -# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" -# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" + ambassador_introduction: "Wir bauen einen Community und du bist die Verbindung dazu. Wir haben Olark Chats, Email und soziale Netzwerke mit vielen Menschen mit denen man sprechen, dabei helfen mit dem Spiel vertraut zu werden und von lernen kann. Wenn du helfen möchtest Leute zu involvieren, Spaß zu haben und ein gutes Gefühl für den Puls von CodeCombat und wo wir ihn wollen, dann könnte diese Klasse für dich sein." + ambassador_attribute_1: "Kommunikation! Du bist fähig, die Probleme die Spieler haben zu erkennen, und ihnen dabei zu helfen, diese zu lösen. Außerdem informierst du uns andere Teammitglieder darüber informiert, was die Spieler beschäftigt, was sie mögen, oder auch nicht, und wovon sie gar nicht genug kriegen!" + ambassador_join_desc: "erzähl uns ein wenig über dich selber, was du so tust, und was du gern tun würdest. Alles Weitere ergibt sich im Gespräch!" ambassador_join_note_strong: "Anmerkung" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" - more_about_ambassador: "Erfahre mehr darüber wie du ein Botschafter werden kannst" - ambassador_subscribe_desc: "Erhalte Emails über Support-Updates and Mehrspieler-Entwicklungen." + ambassador_join_note_desc: "Eine unserer Top Prioritäten ist Mehrspieler Level zu entwerfen. Dort können Spieler, die noch Probleme mit bestimmten Themen haben erfahrenere Zauberer beschwören um ihnen zu helfen. Dies wird ein hervorragender Weg für unsere Botschafter sein, um ihren Job zu erledigen. Wir halten dich auf dem Laufenden!" + ambassador_subscribe_desc: "Erhalte Emails über Support-Updates und Mehrspieler-Entwicklungen." changes_auto_save: "Änderungen an Checkboxen werden automatisch gespeichert." - diligent_scribes: "Unsere fleißgen Schreiber:" + diligent_scribes: "Unsere fleißigen Schreiber:" powerful_archmages: "Unsere mächtigen Erzmagier:" creative_artisans: "Unsere kreativen Handwerker:" brave_adventurers: "Unsere mutigen Abenteurer:" @@ -713,17 +1017,17 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: summary_matches: "Matches - " summary_wins: " Siege, " summary_losses: " Verluste" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" + rank_no_code: "Kein neuer Code zu bewerten" + rank_my_game: "Bewerte mein Spiel!" rank_submitting: "Übermitteln..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" + rank_submitted: "Zur Bewertung übermittelt" + rank_failed: "Bewertung gescheitert" + rank_being_ranked: "Spiel wird bewertet" rank_last_submitted: "übermittelt " help_simulate: "Hilf Spiele zu simulieren?" -# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." + code_being_simulated: "Dein Code wird von anderen Spielern für die Bewertung simuliert. Sobald neue Ergebnisse reinkommen, wird dies aktualisiert." + no_ranked_matches_pre: "Keine bewerteten Ergebnisse für" + no_ranked_matches_post: " Team! Spiele gegen einige Konkurrenten und komm dann hierher zurück um deine Spiele bewertet zu bekommen." choose_opponent: "Wähle einen Gegner" select_your_language: "Wähle deine Sprache!" tutorial_play: "Spiele Tutorial" @@ -734,17 +1038,19 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: simple_ai: "Einfache KI" warmup: "Aufwärmen" friends_playing: "spielende Freunde" - log_in_for_friends: "Melde dich an um mit deinen Freunden zu spielen!" + log_in_for_friends: "Melde dich an, um mit deinen Freunden zu spielen!" social_connect_blurb: "Verbinde und spiele gegen deine Freunde!" invite_friends_to_battle: "Lade deine Freunde zum Kampf ein!" fight: "Kämpft!" watch_victory: "Schau dir deinen Sieg an" defeat_the: "Besiege den" +# tournament_started: ", started" tournament_ends: "Turnier endet" tournament_ended: "Turnier beendet" tournament_rules: "Turnier-Regeln" tournament_blurb: "Schreibe Code, sammle Gold, erstelle Armeen, zerquetsche Feinde, gewinne Preis und verbessere deine Karriere in unserem 40.000 $ Greed-Turnier! Schau dir die Details" tournament_blurb_criss_cross: "Gewinne Gebote, konstruiere Pfade, trickse Feinde aus, greife Edelsteine ab und verbessere deine Karriere in unserem Criss-Cross-Turnier! Schau dir die Details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" tournament_blurb_blog: "auf unserem Blog an" rules: "Regeln" winners: "Gewinner" @@ -753,38 +1059,68 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: stats: "Statistiken" singleplayer_title: "Einzelspieler Level" multiplayer_title: "Mehrspieler Level" - achievements_title: "Achievements" + achievements_title: "Erfolge" last_played: "Zuletzt gespielt" status: "Status" status_completed: "Vollendet" status_unfinished: "Unvollendet" no_singleplayer: "Noch keine Einzelspieler-Spiele gespielt." no_multiplayer: "Noch keine Mehrspieler-Spiele gespielt." - no_achievements: "Noch keine Achievements verdient." + no_achievements: "Noch keine Erfolge verdient." favorite_prefix: "Lieblingssprache ist " favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." achievements: -# last_earned: "Last Earned" + last_earned: "Zuletzt erhalten" amount_achieved: "Anzahl" - achievement: "Achievement" + achievement: "Erfolge" category_contributor: "Mitwirkender" -# category_ladder: "Ladder" -# category_level: "Level" + category_ladder: "Rangliste" + category_level: "Level" category_miscellaneous: "Sonstiges" category_levels: "Level" category_undefined: "ohne Kategorie" -# current_xp_prefix: "" + current_xp_prefix: "" current_xp_postfix: " Gesamt" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" + new_xp_prefix: "" + new_xp_postfix: " erworben" + left_xp_prefix: "" left_xp_infix: " bis Level " -# left_xp_postfix: "" + left_xp_postfix: "" account: recently_played: "Kürzlich gespielt" no_recent_games: "Keine Spiele in den letzten zwei Wochen gespielt." + payments: "Zahlungen" + purchased: "Gekauft" + subscription: "Abo" + invoices: "Rechnungen" + service_apple: "Apple" + service_web: "Web" + paid_on: "Gezahlt am" + service: "Service" + price: "Preis" + gems: "Edelsteine" + active: "Aktive" + subscribed: "Abonniert" + unsubscribed: "Abbestellt" + active_until: "Aktive bis" + cost: "Kosten" + next_payment: "Nächste Zahlung" + card: "Karte" + status_unsubscribed_active: "Du hast kein Abo, und bekommst keine Rechnung, aber dein Account ist weiterhin aktiv." + status_unsubscribed: "Erhalte Zugang zu neuen Leveln, Helden, Gegenständen und Bonus Edelsteinen mit einem CodeCombat Abo!" + + account_invoices: + amount: "Betrag in US-Dollar" + declined: "Ihre Karte wurde nicht akzeptiert" + invalid_amount: "Bitte gib einen US-Dollar Betrag ein." + not_logged_in: "Melde dich an oder erstelle einen Account um deine Rechnungen an zu sehen." + pay: "Bezahle Rechnung" + purchasing: "Kaufe..." + retrying: "Serverfehler, versuche es erneut." + success: "Erfolgreich bezahlt. Danke!" loading_error: could_not_load: "Fehler beim Laden vom Server" @@ -812,8 +1148,9 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: leaderboard: "Rangliste" user_schema: "Benutzerschema" user_profile: "Benutzerprofil" + patch: "Patch" patches: "Patche" -# patched_model: "Source Document" + patched_model: "Quelldokument" model: "Model" system: "System" systems: "Systeme" @@ -828,7 +1165,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: thang_names: "Thang Namen" files: "Dateien" top_simulators: "Top Simulatoren" -# source_document: "Source Document" + source_document: "Quelldokument" document: "Dokument" sprite_sheet: "Sprite Sheet" employers: "Arbeitgeber" @@ -838,24 +1175,51 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: user_remarks: "Benutzerkommentare" versions: "Versionen" items: "Gegenstände" +# hero: "Hero" heroes: "Helden" - wizard: "Zauberer" achievement: "Achievement" clas: "CLAs" -# play_counts: "Play Counts" + play_counts: "Anzahl Spiele" feedback: "Feedback" + payment_info: "Zahlungsinfo" + campaigns: "Kampagne" + poll: "Umfrage" + user_polls_record: "Umfrageergebnisse" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" delta: added: "hinzugefügt" modified: "modifiziert" +# not_modified: "Not Modified" deleted: "gelöscht" -# moved_index: "Moved Index" + moved_index: "Verschobener Index" text_diff: "Text Diff" merge_conflict_with: "MERGE KONFLIKT MIT" no_changes: "Keine Änderungen" -# guide: -# temp: "Temp" + guide: + temp: "Temp" multiplayer: multiplayer_title: "Mehrspieler Einstellungen" # We'll be changing this around significantly soon. Until then, it's not important to translate. @@ -872,13 +1236,13 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: opensource_intro: "CodeCombat ist Free-to-Play und vollständig Open Source." opensource_description_prefix: "Schau dir " github_url: "unsere GitHub-Seite" - opensource_description_center: " an und mach mit wenn Du möchtest! CodeCombat baut auf duzenden Open Source Projekten auf, und wir lieben sie. Schau dir die Liste in " + opensource_description_center: " an und mach mit wenn Du möchtest! CodeCombat baut auf dutzenden Open Source Projekten auf, und wir lieben sie. Schau dir die Liste in " archmage_wiki_url: "unserem Erzmagier-Wiki" opensource_description_suffix: " an, welche Software dieses Spiel möglich macht." practices_title: "Best Practices" practices_description: "Dies sind unsere Versprechen an dich, den Spieler, in weniger Fachchinesisch." privacy_title: "Datenschutz" - privacy_description: "Wir werden deine persönlichen Daten nicht verkaufen. Letztenendes beabsichtigen wir, durch Vermittlung von Jobs zu verdienen, aber sei versichert, dass wir nicht deine persönlichen Daten ohne deine ausdrückliche Einwilligung interessierten Firmen zur Verfügung stellen werden." + privacy_description: "Wir werden keine deiner persönlichen Informationen verkaufen!" security_title: "Datensicherheit" security_description: "Wir streben an, deine persönlichen Daten sicher zu verwahren. Als Open Source Projekt ist unsere Site frei zugänglich für jedermann, auch um unsere Sicherheitsmaßnahmen in Augenschein zu nehmen und zu verbessern." email_title: "Email" @@ -886,36 +1250,30 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: email_settings_url: "deiner Emaileinstellungen" email_description_suffix: "oder durch von uns gesendete Links kannst du jederzeit deine Einstellungen ändern und Abonnements kündigen." cost_title: "Kosten" - cost_description: "CodeCombat ist zur Zeit 100% kostenlos! Eines unserer Hauptziele ist, es dabei zu belassen, so dass es so viele Leute wie möglich spielen können, unabhängig davon in welcher Lebenssituation sie sich befinden. Falls dunkle Wolken aufziehen, könnten wir manche Inhalte im Rahmen eines Abonnements anbieten, aber lieber nicht. Mit etwas Glück können wir die Firma erhalten durch:" - recruitment_title: "Recruiting" - recruitment_description_prefix: "Hier bei CodeCombat kannst du ein mächtiger Zauberer werden, nicht nur im Spiel, sondern auch in der Realität." - url_hire_programmers: "Niemand kann schnell genug Programmierer einstellen." - recruitment_description_suffix: "So wenn du deine Fähigkeiten entwickelt hast und zustimmst, werden wir deine besten Leistungen den tausenden Arbeitgebern demonstrieren, welche nur auf die Gelegentheit warten, dich einzustellen. Sie bezahlen uns ein bisschen, und sie bezahlen dir " - recruitment_description_italic: "jede Menge" - recruitment_description_ending: ", die Seite bleibt kostenlos und jeder ist glücklich. So der Plan." + cost_description: "Alle normalen Level von CodeCombat sind kostenlos spielbar, mit einem Abonnement von $9.99 USD/Monat kann man extra Level Verzweigungen und 3500 Bonus Juwelen pro Monat. Du kannst das Abonnement mit nur einem Klick widerrufen und wir versprechen eine 100% Geld-zurück Garantie." copyrights_title: "Copyrights und Lizenzen" contributor_title: "Contributor License Agreement" contributor_description_prefix: "Alle Beiträge, sowohl auf unserer Webseite als auch in unserem GitHub Repository, unterliegen unserer" cla_url: "CLA" - contributor_description_suffix: "zu welcher du dich einverstanden erklären musst bevor du beitragen kannst." + contributor_description_suffix: "zu welcher du dich einverstanden erklären musst bevor du zu der Entwicklung beitragen kannst." code_title: "Code - MIT" - code_description_prefix: "Der gesamte Code der CodeCombat gehört oder auf codecombat.com gehostet wird, sowohl im GitHub Repository als auch auch in der codecombat.com Datenbank, ist lizensiert durch die" + code_description_prefix: "Der gesamte Code der CodeCombat gehört oder auf codecombat.com gehostet wird, sowohl im GitHub Repository als auch auch in der codecombat.com Datenbank, ist lizenziert durch die" mit_license_url: "MIT Lizenz" - code_description_suffix: "Dies beihnhaltet all den Code in Systemen und Komponenten der für die Erstellung von Levels durch CodeCombat zu Verfügung gestellt wird." + code_description_suffix: "Dies beinhaltet all den Code in Systemen und Komponenten der für die Erstellung von Levels durch CodeCombat zu Verfügung gestellt wird." art_title: "Grafiken/Musik - Creative Commons " art_description_prefix: "Gemeinsamer Inhalt ist verfügbar unter" cc_license_url: "Creative Commons Attribution 4.0 International License" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" + art_description_suffix: "Gemeinsamer Inhalt ist alles, was durch CodeCombat für den Zweck der Erstellung von Levels allgemein verfügbar gemacht wird. Dies beinhaltet:" art_music: "Musik" art_sound: "Sound" art_artwork: "Grafiken" art_sprites: "Sprites" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." -# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." -# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" + art_other: "Jegliche oder alle kreative nicht-Code Arbeit, die zur Verfügung gestellt wird, wenn Level erstellt werden." + art_access: "Derzeit gibt es kein universelles, einfaches System, um diese Assets zu holen. Generell kannst du sie von den URLs holen, die auf der Seite verwendet werden. Kontaktiere uns, wenn du hilfe brauchst, oder um uns zu helfen, die Seite auszubauen, um diese Assets leichter zugänglich zu machen." + art_paragraph_1: "Für den Verweis auf CodeCombat, nenne und verlinke bitte die Website codecombat.com nahe der Quelle oder an der Stelle, wo es für das Medium angemessen ist. Zum Beispiel:" use_list_1: "Wenn in einem Film verwendet, nenne codecombat.com in den Credits/Abspann" -# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." -# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." + use_list_2: "Wenn auf einer Webseite verwendet, füge einen Link nahe bei der Verwendung ein, z.B. unter einem Bild oder auf der generellen Beitragsseite, wo auch andere Creative Commons Werke und Open Source Software genannt wird, die auf der Seite verwendet wird. Wenn deutlich auf CodeCombat Bezug genommen wird, wie z.B. in einem Blogeintrag, in dem CodeCombat erwähnt wird, dann muss CodeCombat nicht separat belegt werden." + art_paragraph_2: "Wenn der benutzte Inhalt nicht von CodeCombat sonder einem Benutzer von codecombat.com geschaffen wurde, schreibe es diesem stattdessen zu und folge den Anweisungen, wenn es welche gibt, in der Beschreibung dieses Inhalts." rights_title: "Rechte vorbehalten" rights_desc: "Alle Rechte vorbehalten für die Level selbst. Dies beinhaltet" rights_scripts: "Skripte" @@ -923,18 +1281,18 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: rights_description: "Beschreibung" rights_writings: "Schriftliches" rights_media: "Medien (Sounds, Musik) und jede andere Form von kreativem Inhalt der spezifisch für das Level ist nicht generell für die Levelerstellung bereitgestellt wird." -# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." + rights_clarification: "Zur Klarstellung: Alles, was im Level Editor zur Verfügung steht, um Levels zu erstellen, unterliegt CC; jedoch nicht der Content, der mit dem Level Editor erstellt wird oder im Rahmen der Erstellung von Levels hochgeladen wird." nutshell_title: "Zusammenfassung" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." + nutshell_description: "Jegliche Ressourcen, die wir im Level Editor zur Verfügung stellen, sind für dich frei nutzbar, um Levels zu erstellen. Doch wir behalten uns das Recht vor, die Verbreitung der Levels (welche auf codecombat.com erstellt werden) einzuschränken, so dass in der Zukunft dafür gezahlt werden muss, wenn es das ist, was letztendlich passieren wird." canonical: "Die englische Version dieses Dokuments ist die definitive, kanonische Version. Sollte es Unterschiede zwischen den Übersetzungen geben, dann hat das englische Dokument Vorrang." ladder_prizes: title: "Turnierpreise" # This section was for an old tournament and doesn't need new translations now. blurb_1: "Die Preise werden verliehen nach" blurb_2: "den Turnierregeln" - blurb_3: "and den Top Mensch und Oger-Spieler." + blurb_3: "und dem Top Mensch und Oger-Spieler." blurb_4: "Zwei Teams heißt die doppelte Anzahl zu gewinnender Preise!" - blurb_5: "(Es wird zwei Erstplazierte, zwei Zeitplatzierte, usw. geben)" + blurb_5: "(Es wird zwei Erstplazierte, zwei Zweitplatzierte, usw. geben)" rank: "Rang" prizes: "Gewinne" total_value: "Gesamtwert" @@ -948,30 +1306,14 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: license: "Lizenz" oreilly: "Ebook deiner Wahl" - wizard_settings: - title: "Zauberer Einstellungen" - customize_avatar: "Individualisiere deinen Avatar" - active: "Aktiv" - color: "Farbe" - group: "Gruppe" - clothes: "Kleidung" - trim: "Applikationen" - cloud: "Wolke" - team: "Team" - spell: "Zauber" - boots: "Stiefel" - hue: "Farbton" - saturation: "Sättigung" - lightness: "Helligkeit" - account_profile: settings: "Einstellungen" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Profil editieren" - done_editing: "Editierung beenden" + done_editing: "Bearbeitung beenden" profile_for_prefix: "Profil von " profile_for_suffix: "" -# featured: "Featured" -# not_featured: "Not Featured" + featured: "Featured" + not_featured: "Not Featured" looking_for: "Suche nach:" last_updated: "zuletzt geändert:" contact: "Kontakt" @@ -1020,19 +1362,19 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: name_anonymous: "Anonymer Entwickler" name_help: "Name den Arbeitgeber sehen sollen, z.B. 'Nick Winter'." short_description_header: "Schreibe einen kurzen Text über dich" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" + short_description_blurb: "Füge eine Tagline hinzu, damit Arbeitgeber schnell mehr über dich erfahren können." + short_description: "Tagline" short_description_help: "Wer bist du und wonach suchst du? 140 Zeichen max." skills_header: "Fähigkeiten" -# skills_help: "Tag relevant developer skills in order of proficiency." + skills_help: "Markiere relevante Entwicklerfähigkeiten in der Reihenfolge deines Niveaus." long_description_header: "Beschreibe deine gewünschte Position" - long_description_blurb: "Teile Arbeitgebern mit wie toll du bist und nach welcher Position du suchst." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." + long_description_blurb: "Teile Arbeitgebern mit, wie toll du bist und nach welcher Position du suchst." + long_description: "Selbstbeschreibung" + long_description_help: "Beschreibe dich selbst für potentielle Arbeitgeber. Fasse dich kurz, aber treffend. Wir empfehlen die Position kurz darzustellen, die dich am meisten interessieren würde. Geschmackvoller Preisabschlag ist ok; max. 600 Zeichen." work_experience: "Berufserfahrung" work_header: "Liste deine Berufserfahrung chronologisch auf" work_years: "Jahre der Erfahrung" - work_years_help: "Wieviele Jahre professionelle Erfahrung (bezahlte Arbeit) hast du mit der Entwicklung von Software?" + work_years_help: "Wie viele Jahre professionelle Erfahrung (bezahlte Arbeit) hast du mit der Entwicklung von Software?" work_blurb: "Liste deine relevante Berufserfahrung auf, letzte Tätigkeit zuerst." work_employer: "Arbeitgeber" work_employer_help: "Name deines Arbeitgebers." @@ -1052,13 +1394,13 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: education_duration: "Daten" education_duration_help: "Wann?" education_description: "Beschreibung" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" + education_description_help: "Hebe etwas aus dieser Ausbildung hervor. (140 Zeichen; optional)" our_notes: "CodeCombat's Notizen" remarks: "Kommentare" projects: "Projekte" projects_header: "Füge 3 Projekte hinzu" projects_header_2: "Projekte (Top 3)" - projects_blurb: "Hebe deine Projekte hervor um Arbeitgeber zu verblüffen." + projects_blurb: "Hebe deine Projekte hervor, um Arbeitgeber zu verblüffen." project_name: "Projekt Name" project_name_help: "Wie wurde das Projekt genannt?" project_description: "Beschreibung" @@ -1066,19 +1408,19 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: project_picture: "Bild" project_picture_help: "Lade ein 230x115px oder größeres Bild hoch, welches das Projekt darstellt." project_link: "Link" - project_link_help: "Verlinke zu dem Projekt." + project_link_help: "Verlinke das Projekt." player_code: "Spieler Code" employers: -# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." -# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." + deprecation_warning_title: "Leider sucht CodeCombat derzeit keine neuen Mitarbeiter." + deprecation_warning: "Wir konzentrieren uns derzeit auf Anfängerlevel, statt Expertendeveloper zu finden." hire_developers_not_credentials: "Stellen Sie Entwickler ein, nicht Qualifikationen." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. get_started: "Legen Sie los" already_screened: "Wir haben alle Kandidaten bereits technisch geprüft" filter_further: ", aber Sie können noch weiter filtern:" filter_visa: "Visum" - filter_visa_yes: "US authorisiert" - filter_visa_no: "nicht authorisiert" + filter_visa_yes: "US autorisiert" + filter_visa_no: "nicht autorisiert" filter_education_top: "Top-Schule" filter_education_other: "Andere" filter_role_web_developer: "Web Entwickler" @@ -1093,33 +1435,33 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: start_hiring: "Beginnen Sie einzustellen." reasons: "Drei Gründe warum Sie durch uns einstellen sollten:" everyone_looking: "Jeder sucht hier nach der nächsten Herausforderung." - everyone_looking_blurb: "Vergessen Sie die 20% LinkedIn InMail Antwort-Raten. Jeden den wir auf unseren Webseite listen, sucht nach seiner nächsten Position und wird Ihnen auf Ihren Wunsch zu einem weiteren kennenlernen antworten." + everyone_looking_blurb: "Vergessen Sie die 20% LinkedIn InMail Antwort-Raten. Jeden den wir auf unseren Webseite auflisten, sucht nach seinem nächsten Arbeitsplatz und wird Ihnen auf Ihre Kontaktanfrage antworten." weeding: "Lehnen Sie sich zurück; wir haben das Aussortieren für Sie übernommen." - weeding_blurb: "Jeden Spieler den wir listen wurde bereits auf seine technischen Fähigkeiten geprüft. Wir führen auch Telefoninterviews mit ausgewählten Kandidaten durch und machen Notizen in ihrem Profil um Ihnen Zeit zu ersparen." + weeding_blurb: "Jeden Spieler, den wir auflisten, wurde bereits auf seine technischen Fähigkeiten geprüft. Wir führen auch Telefoninterviews mit ausgewählten Kandidaten durch und machen Notizen in ihrem Profil, um Ihnen Zeit zu ersparen." pass_screen: "Sie werden Ihre technischen Tests bestehen." - pass_screen_blurb: "Prüfen Sie den Code jedes Kandidaten bevor Sie ihn kontaktieren. Ein Arbeitgeber fand heraus das bis zu 5 mal soviele unserer Entwickler sein technisches Screening bestanden als wenn er durch Hacker News eingestellt hätte." + pass_screen_blurb: "Prüfen Sie den Code jedes Kandidaten bevor Sie ihn kontaktieren. Ein Arbeitgeber fand heraus das bis zu 5 mal so viele unserer Entwickler sein technisches Screening bestanden als wenn er durch Hacker News eingestellt hätte." make_hiring_easier: "Machen Sie bitte meinen Einstellungsprozess einfachen." what: "Was ist CodeCombat?" - what_blurb: "CodeCombat ist ein Mehrspieler Browser-Programmierspiel. Spieler schreiben Code um Ihre Armeen im Kampf gegen andere Entwickler zu steuern. Unsere Spieler habe Erfahrung mit allen wichtigen Tech-Stacks." - cost: "Wieviel berechnen wir?" - cost_blurb: "Wir berechnen 15% des ersten Jahresgehalts und bieten eine 100% Geld-Zurück-Garantie für 90 Tage an. Wir berechnen nicht für Kandidaten die bereits aktiv an Ihrem Bewerbungsverfahren teilnehmen." + what_blurb: "CodeCombat ist ein Mehrspieler Browser-Programmierspiel. Spieler schreiben Code, um Ihre Armeen im Kampf gegen andere Entwickler zu steuern. Unsere Spieler haben Erfahrung mit allen wichtigen Tech-Stacks." + cost: "Wie viel berechnen wir?" + cost_blurb: "Wir berechnen 15% des ersten Jahresgehalts und bieten eine 100% Geld-Zurück-Garantie für 90 Tage an. Wir berechnen nicht für Kandidaten, die bereits aktiv an Ihrem Bewerbungsverfahren teilnehmen." candidate_name: "Name" candidate_location: "Standort" candidate_looking_for: "Sucht nach" candidate_role: "Rolle" candidate_top_skills: "Top Fähigkeiten" -# candidate_years_experience: "Yrs Exp" + candidate_years_experience: "Erfahrung (Jahre)" candidate_last_updated: "Zuletzt aktualisiert" candidate_who: "Wer" -# featured_developers: "Featured Developers" + featured_developers: "Mitwirkende Entwickler" other_developers: "Andere Entwickler" - inactive_developers: "Inaktive Etwickler" + inactive_developers: "Inaktive Entwickler" admin: av_espionage: "Spionage" # Really not important to translate /admin controls. av_espionage_placeholder: "Email oder Benutzername" av_usersearch: "Benutzersuche" - av_usersearch_placeholder: "Email, Benutzename, Name, Was auch immer" + av_usersearch_placeholder: "Email, Benutzename, Name, was auch immer" av_usersearch_search: "Suchen" av_title: "Administrator Übersicht" av_entities_sub_title: "Entitäten" @@ -1127,10 +1469,10 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: av_entities_active_instances_url: "Aktive Instanzen" av_entities_employer_list_url: "Arbeitgeberliste" av_entities_candidates_list_url: "Kandidatenliste" -# av_entities_user_code_problems_list_url: "User Code Problems List" + av_entities_user_code_problems_list_url: "User Code Problem-Liste" av_other_sub_title: "Sonstige" av_other_debug_base_url: "Base (um base.jade zu debuggen)" u_title: "Benutzerliste" -# ucp_title: "User Code Problems" + ucp_title: "User Code Probleme" lg_title: "Letzte Spiele" clas: "CLAs" diff --git a/app/locale/el.coffee b/app/locale/el.coffee index 1a75aff2c..9491cb10b 100644 --- a/app/locale/el.coffee +++ b/app/locale/el.coffee @@ -1,22 +1,23 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Greek", translation: home: slogan: "Μάθε να Προγραμμάτιζεις Παίζοντας ένα Παιχνίδι" - no_ie: "Το CodeCombat δεν τρέχει σε Internet Explorer 9 ή παλαιότερη έκδοση. Συγνώμη!" # Warning that only shows up in IE8 and older + no_ie: "Το CodeCombat δεν είναι συμβατό με το Internet Explorer 9 ή κάποια παλαιότερη έκδοση. Συγνώμη!" # Warning that only shows up in IE8 and older no_mobile: "Το CodeCombat δεν σχεδιάστηκε για κινητά και μπορεί να μην δουλεύει!" # Warning that shows up on mobile devices - play: "Παίξε" # The big play button that just starts playing a level + play: "Παίξε" # The big play button that opens up the campaign view. old_browser: "Ωχ, ο περιηγητής σας είναι πολύ παλιός για να τρέξει το CodeCombat. Συγνώμη!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Μπορείτε να δοκιμάσετε, αλλά πιθανότατα να μην λειτουργήσει." + ipad_browser: "Δυσάρεστα νέα: Το CodeCombat δεν τρέχει στο iPad μέσω περιηγητή. Ευχάριστα νέα: αναμένουμε την έγκριση της Apple για την εφαρμογή μας για iPad." campaign: "Εκστρατεία" for_beginners: "Για αρχάριους" multiplayer: "Πολλαπλοί Παίκτες" # Not currently shown on home page for_developers: "Για προγραμματιστές" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Η' κατεβάστε για iPad" nav: play: "Επίπεδα" # The top nav bar entry where players choose which levels to play community: "Κοινότητα" editor: "Συγγραφέας" - blog: "Μπλόγκ" + blog: "Ιστολόγιο" forum: "Φόρουμ" account: "Λογαριασμός" profile: "Προφίλ" @@ -26,13 +27,13 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre home: "Αρχική" contribute: "Συνεισφέρω" legal: "Νομικά" - about: "Σχετικά με" + about: "Περί" contact: "Επικοινωνία" twitter_follow: "Ακολούθησε" -# teachers: "Teachers" + teachers: "Εκπαιδευτικοί" modal: - close: "Κλείσε" + close: "Κλείσιμο" okay: "Εντάξει" not_found: @@ -41,9 +42,9 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre diplomat_suggestion: title: "Βοηθήστε στην μετάφραση του CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "Χρειαζόμαστε τις γλωσσικές σας δεξιότητες." - pitch_body: "Αναπτύσσουμε το CodeCombat στα Αγγλικά, αλλά ήδη έχουμε παίκτες από όλο τον κόσμο. Πολλοί από αυτούς θέλουν να παίξουν στα Ελληνικά, αλλά δεν μιλούν Αγγλικά, οπότε αν μπορείτε να μιλήσετε και τις δύο, παρακαλούμε να σκεφτείτε την εγγραφή ως Διπλωμάτης και να βοηθήσετε να μεταφραστεί τόσο η ιστοσελίδα CodeCombat και όλα τα επίπεδα στην Ελληνική." + pitch_body: "Αναπτύσσουμε το CodeCombat στα αγγλικά, αλλά ήδη έχουμε παίκτες από όλο τον κόσμο. Πολλοί από αυτούς θέλουν να παίξουν στα ελληνικά, αλλά δεν μιλούν αγγλικά. Εάν έχετε ευχέρεια και στις δύο γλώσσες, αγγλική κι ελληνική, παρακαλούμε να εγγραφείτε ως Διπλωμάτης και να συμβάλετε στην απόδοση της ιστοσελίδας CodeCombat και όλων των επιπέδων στην ελληνική γλώσσα." missing_translations: "Μέχρι να μπορούν να μεταφράσουν τα πάντα σε Ελληνικά, θα δείτε την αγγλική γλώσσα όπου τα Ελληνικά δεν είναι διαθέσιμα." - learn_more: "Μάθετε περισσότερα σχετικά με το να είστε ένας Διπλωμάτης" + learn_more: "Μάθετε πώς να γίνετε Διπλωμάτης" subscribe_as_diplomat: "Εγγραφή ως Διπλωμάτης" play: @@ -52,66 +53,64 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre players: "παίκτες" # Hover over a level on /play hours_played: "ώρες παιχνιδιού" # Hover over a level on /play items: "Αντικείμενα" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" + unlock: "Ξεκλειδώστε" # For purchasing items and heroes + confirm: "Επιβεβαίωση" # owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" + locked: "Κλειδωμένο" + purchasable: "Διαθέσιμο για αγορά" # For a hero you unlocked but haven't purchased + available: "Διαθέσιμο" # skills_granted: "Skills Granted" # Property documentation details heroes: "Ήρωες" # Tooltip on hero shop button from /play achievements: "Επιτεύγματα" # Tooltip on achievement list button from /play account: "Λογαριασμός" # Tooltip on account button from /play settings: "Ρυθμίσεις" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play next: "Επόμενο" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero + change_hero: "Αλλαγή Ήρωα" # Go back from choose inventory to choose hero choose_inventory: "Εξοπλίσου με Αντικείμενα" -# buy_gems: "Buy Gems" - older_campaigns: "Παλαιότερες Εκστρατείες" + buy_gems: "Αγορά Διαμαντιών" + subscription_required: "Απαιτείται εγγραφή" anonymous: "Ανώνυμοι Παίκτες" level_difficulty: "Δυσκολία: " campaign_beginner: "Εκστρατεία για Αρχάριους" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Διάλεξε το επίπεδο σου" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Μπορείτε να μεταβείτε σε οποιοδήποτε επίπεδο κάτω, ή να συζητήσετε για τις πίστες στο " - adventurer_forum: "Φόρουμ του Adventurer" - adventurer_suffix: "." - campaign_old_beginner: "Παλαιότερη Εκστρατεία Αρχαρίων" - campaign_old_beginner_description: "... στην οποία μαθαίνετε τη μαγεία του προγραμματισμού." - campaign_dev: "Τυχαία Δυσκολότερα Επίπεδα" - campaign_dev_description: "... στα οποία μπορείτε να μάθετε το περιβάλλον, ενώ κάνετε κάτι λίγο δυσκολότερο." +# adjust_volume: "Adjust volume" campaign_multiplayer: "Αρένες Πολλαπλών Παικτών" campaign_multiplayer_description: "... στις οποίες προγραμματίζετε σώμα-με-σώμα εναντίον άλλων παικτών." - campaign_player_created: "Δημιουργημένη-από-Παίκτες" - campaign_player_created_description: "... στην οποία μάχεστε ενάντια στην δημιουργικότητα των συναδέλφων <a href=\"/contribute#artisan\">Τεχνιτών Μάγων</a>." - campaign_classic_algorithms: "Κλασσικοί Αλγόριθμοι" - campaign_classic_algorithms_description: "... στο οποίο μαθαίνετε του πιο δημοφιλής αλγορίθμους της Επιστήμης της Πληροφορικής." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" + form_label: "Διεύθυνση ηλεκτρονικού ταχυδρομίου" + placeholder: "Διεύθυνση ηλεκτρονικού ταχυδρομίου" + title: "Εξαιρετική Δουλειά, Μαθητευόμενε" login: sign_up: "Δημιουργία Λογαριασμού" log_in: "Σύνδεση" - logging_in: "Σύνδεση" + logging_in: "Σύνδεση σε εξέλιξη" log_out: "Αποσύνδεση" - recover: "Ανάκτηση λογαριασμού" + forgot_password: "Ξεχάσατε τον κωδικό σας;" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" + sign_in_with_facebook: "Σύνδεση μέσω Facebook" + sign_in_with_gplus: "Σύνδεση μέσω G+" +# signup_switch: "Want to create an account?" signup: - create_account_title: "Δημιουργία λογαριασμού για Αποθήκευση της Προόδου" - description: "Είναι δωρεάν. Απλώς χρειάζεται λίγα πράγματα και θα είσαι έτοιμος να παίξεις:" email_announcements: "Λαμβάνετε ανακοινώσεις μέσω email" - coppa: "13+ ή Εκτός Αμερικής " - coppa_why: "(Γιατί;)" creating: "Δημιουργία Λογαριασμού..." sign_up: "Εγγραφή" log_in: "Σύνδεση με κωδικό" social_signup: "Ή, μπορείς να συνδεθείς μέσω Facebook ή G+:" required: "Θα πρέπει να συνδεθείτε πριν πάτε προς τα εκεί." +# login_switch: "Already have an account?" recover: recover_account_title: "Ανάκτηση λογαριασμού" @@ -124,9 +123,11 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre armor: "Πανοπλία" accessories: "Εξαρτήματα" misc: "Διάφορα" -# books: "Books" + books: "Βιβλία" common: + back: "Προηγούμενο" # When used as an action verb, like "Navigate backward" + continue: "Συνέχεια" # When used as an action verb, like "Continue forward" loading: "Φορτώνει..." saving: "Αποθήκευση..." sending: "Αποστολή..." @@ -135,23 +136,41 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre save: "Αποθήκευση" publish: "Δημοσίευση" create: "Δημιουργία" -# manual: "Manual" + manual: "Οδηγίες" # fork: "Fork" -# play: "Play" # When used as an action verb, like "Play next level" + play: "Παίξε" # When used as an action verb, like "Play next level" # retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" +# actions: "Actions" +# info: "Info" +# help: "Help" + watch: "Παρακολούθηση" + unwatch: "Παύση παρακολούθηση" + submit_patch: "Αποστολή Επιδιόρθωσης" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" general: and: "και" name: "Όνομα" -# date: "Date" + date: "Ημερομηνία" # body: "Body" version: "Έκδοση" + pending: "Εκκρεμεί" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" result: "Αποτέλεσμα" results: "Αποτελέσματα" description: "Περιγραφή" @@ -160,7 +179,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre email: "Email" password: "Κωδικός" message: "Μήνυμα" -# code: "Code" + code: "Κώδικας" # ladder: "Ladder" when: "Όταν" opponent: "Αντίπαλος" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre hard: "Δύσκολο" player: "Παίκτης" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" + wizard: "Μάγος" units: second: "δευτερόλεπτο" @@ -194,7 +216,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre play_level: done: "Έτοιμο" home: "Αρχική" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" + level: "Επίπεδο" # Like "Level: Dungeons of Kithgard" skip: "Παράλειψη" game_menu: "Μενού Παιχνιδιού" guide: "Οδηγός" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre reload_title: "Ανανέωση όλου του κωδικά;" reload_really: "Είστε σίγουροι ότι θέλετε να φορτώσετε αυτό το επίπεδο από την αρχή;" reload_confirm: "Ανανέωση όλων" + victory: "Νίκη" # victory_title_prefix: "" # victory_title_suffix: " Complete" victory_sign_up: "Εγγραφείτε για ενημερώσεις" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre victory_rate_the_level: "Βαθμολογήστε το επίπεδο: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Παίξε το επόμενο επίπεδο" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" victory_go_home: "Πηγαίνετε στην Αρχική" # Only in old-style levels. victory_review: "Πείτε μας περισσότερα!" # Only in old-style levels. victory_hour_of_code_done: "Τελείωσες;" victory_hour_of_code_done_yes: "Ναι, έχω τελειώσει με την Hour of Code!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Οδηγός" tome_minion_spells: "Ξόρκια για τα τσιράκια σας" # Only in old-style levels. tome_read_only_spells: "Ξορκια μονο για αναγνωση" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Προσαρμογή Μάγου" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" multiplayer_tab: "Πολλαπλοί παίχτες" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre lua_blurb: "Scripting γλώσσα παιχνιδιών." io_blurb: "Απλή αλλά ασαφής." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,37 +562,131 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" -# versions: -# save_version_title: "Save New Version" -# new_major_version: "New Major Version" -# cla_prefix: "To save changes, first you must agree to our" -# cla_url: "CLA" -# cla_suffix: "." -# cla_agree: "I AGREE" +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." + + versions: + save_version_title: "Αποθήκευση Νέας Έκδοσης" + new_major_version: "Δημιουργία Νέας Έκδοσης" +# submitting_patch: "Submitting Patch..." + cla_prefix: "Για να αποθηκευτούν οι αλλαγές, χρειάζεται πρώτα να συμφωνήσεις με την " + cla_url: "Άδεια Χρήσης Συντελεστή (CLA)" + cla_suffix: "." + cla_agree: "ΣΥΜΦΩΝΩ" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Επικοινωνήστε μαζί μας" - welcome: "Καλό να ακούσω από εσάς! Χρησιμοποιήστε αυτή τη φόρμα για να μας στείλετε email. " - contribute_prefix: "Αν σας ενδιαφέρει να βοηθήσετε, ελέγξτε την " - contribute_page: "σελίδα συνεισφοράς" - contribute_suffix: "!" + welcome: "Καλό είναι να έχουμε νέα από εσάς! Χρησιμοποιήστε αυτή τη φόρμα για να μας στείλετε email. " forum_prefix: "Για οτιδήποτε δημόσιο, παρακαλούμε δοκίμαστε " forum_page: "το φόρουμ μας" - forum_suffix: "" + forum_suffix: "." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Αποστολή σχολίων" -# contact_candidate: "Contact Candidate" # Deprecated -# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated + contact_candidate: "Επικοινώνησε με τον Υποψήφιο" # Deprecated + recruitment_reminder: "Χρησιμοποίησε τη φόρμα αυτή για να επικοινωνήσεις με τους υποψήφιους, από τους οποίους ενδιαφέρεσαι να πάρεις συνέντευξη. Υπενθυμίζουμε πως το CodeCombat χρεώνει το 15% της αμοιβής του πρώτου έτους. Η καταβολή του ποσού γίνεται με την πρόσληψη του εργαζομένου, και μπορεί να επιστραφεί εντός 90 ημερών, στην περίπτωση που ο εργαζόμενος δεν παραμείνει στην εργασία. Οι μερικής απασχόλησης, οι εργαζόμενοι εξ αποστάσεως (τηλε-εργασία), οι μπλοκάκηδες και οι πρακτικάριοι είναι δωρεάν." # Deprecated account_settings: title: "Ρυθμίσεις λογαριασμού" @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre autosave: "Οι ρυθμίσεις αποθηκεύονται αυτόματα" me_tab: "Εγώ" picture_tab: "Φωτογραφία" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" upload_picture: "Ανέβασμα φωτογραφίας" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Κωδικός" emails_tab: "Emails" admin: "Διαχειριστής" new_password: "Καινούργιος Κωδικός" new_password_verify: " Επαλήθευση Κωδικού" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "Συνδρομές Email" # email_subscriptions_none: "No Email Subscriptions." email_announcements: "Ανακοινώσεις" @@ -468,7 +719,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # email_recruit_notes: "Job Opportunities" # email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." contributor_emails: "Contributor Class Emails" - contribute_prefix: "Αναζητούμε για ανθρώπους που θέλουν να " + contribute_prefix: "Αναζητούμε ανθρώπους που θέλουν να " contribute_page: "Σελίδα συνεισφοράς" contribute_suffix: " μάθε περισσότερα" email_toggle: "Επέλεξα όλα" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" - wizard_tab: "Μάγος" - wizard_color: "Χρώμα ρούχων του Μάγου" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "Αρχιμάγος" archmage_title_description: "(Προγραμματιστής)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "Τεχνίτης" artisan_title_description: "(Δημιουργός επιπέδων)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "Εξερευνητής" - adventurer_title_description: "(Δοκιματής επιπέδων)" + adventurer_title_description: "(Δοκιμαστής επιπέδων)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Γραφέας" scribe_title_description: "(Συντάκτης άρθρων)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "Διπλωμάτης" diplomat_title_description: "(Μεταφραστής)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "Πρεσβευτής" ambassador_title_description: "(Υποστήριξη)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Ρυθμίσεις Μάγου" - customize_avatar: "Διαμόρφωσε το Avatar σου" - active: "Ενεργό" - color: "Χρώμα" - group: "Ομάδα" - clothes: "Ρούχα" - trim: "Τελείωμα" - cloud: "Σύννεφο" - team: "Ομάδα" - spell: "Ξόρκι" - boots: "Μπότες" - hue: "Απόχρωση" - saturation: "Κορεσμός" - lightness: "Φωτεινότητα" - account_profile: settings: "Ρυθμίσεις" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Επεξεργασία προφίλ" diff --git a/app/locale/en-GB.coffee b/app/locale/en-GB.coffee index 9231d7c1a..9e30f777a 100644 --- a/app/locale/en-GB.coffee +++ b/app/locale/en-GB.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # slogan: "Learn to Code by Playing a Game" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level +# play: "Play" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" # login: # sign_up: "Create Account" # log_in: "Log In" # logging_in: "Logging In" # log_out: "Log Out" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -126,8 +125,10 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # misc: "Misc" # books: "Books" - common: - loading: "Loading..." +# common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" +# loading: "Loading..." # saving: "Saving..." # sending: "Sending..." # send: "Send" @@ -139,9 +140,14 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # fork: "Fork" # play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -191,7 +213,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # year: "year" # years: "years" - play_level: +# play_level: # done: "Done" # home: "Home" # Not used any more, will be removed soon. # level: "Level" # Like "Level: Dungeons of Kithgard" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Customise Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,50 +562,151 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated - account_settings: +# account_settings: # title: "Account Settings" # not_logged_in: "Log in or create an account to change your settings." # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" - wizard_color: "Wizard Clothes Colour" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "Interested in working on game graphics, user interface design, database and server organisation, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." archmage_introduction: "One of the best parts about building games is they synthesise so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalisation developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." copyrights_title: "Copyrights and Licences" contributor_title: "Contributor Licence Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # license: "license" # oreilly: "ebook of your choice" - wizard_settings: -# title: "Wizard Settings" - customize_avatar: "Customise Your Avatar" -# active: "Active" - color: "Colour" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/en-US.coffee b/app/locale/en-US.coffee index 440d59d2b..15ce28301 100644 --- a/app/locale/en-US.coffee +++ b/app/locale/en-US.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # slogan: "Learn to Code by Playing a Game" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level +# play: "Play" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" # login: # sign_up: "Create Account" # log_in: "Log In" # logging_in: "Logging In" # log_out: "Log Out" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Loading..." # saving: "Saving..." # sending: "Sending..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # fork: "Fork" # play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/en.coffee b/app/locale/en.coffee index b60de8fbd..f894dacbb 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -3,9 +3,10 @@ slogan: "Learn to Code by Playing a Game" no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices - play: "Play" # The big play button that just starts playing a level + play: "Play" # The big play button that opens up the campaign view. old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "You can try anyway, but it probably won't work." + ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." campaign: "Campaign" for_beginners: "For Beginners" multiplayer: "Multiplayer" # Not currently shown on home page @@ -63,56 +64,53 @@ achievements: "Achievements" # Tooltip on achievement list button from /play account: "Account" # Tooltip on account button from /play settings: "Settings" # Tooltip on settings button from /play + poll: "Poll" # Tooltip on poll button from /play next: "Next" # Go from choose hero to choose inventory before playing a level change_hero: "Change Hero" # Go back from choose inventory to choose hero choose_inventory: "Equip Items" buy_gems: "Buy Gems" - older_campaigns: "Older Campaigns" + subscription_required: "Subscription Required" anonymous: "Anonymous Player" level_difficulty: "Difficulty: " campaign_beginner: "Beginner Campaign" - awaiting_levels_adventurer_prefix: "We release five levels per week." + awaiting_levels_adventurer_prefix: "We release new levels every week." awaiting_levels_adventurer: "Sign up as an Adventurer" awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "You can jump to any level below, or discuss the levels on " - adventurer_forum: "the Adventurer forum" - adventurer_suffix: "." - campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... in which you learn the wizardry of programming." - campaign_dev: "Random Harder Levels" - campaign_dev_description: "... in which you learn the interface while doing something a little harder." + adjust_volume: "Adjust volume" campaign_multiplayer: "Multiplayer Arenas" campaign_multiplayer_description: "... in which you code head-to-head against other players." - campaign_player_created: "Player-Created" - campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." - campaign_classic_algorithms: "Classic Algorithms" - campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." - campaign_forest: "Forest Campaign" - campaign_dungeon: "Dungeon Campaign" + campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" + campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." + email_invalid: "Email address invalid." + form_blurb: "Enter your parent's email below and we’ll show them!" + form_label: "Email Address" + placeholder: "email address" + title: "Excellent Work, Apprentice" login: sign_up: "Create Account" log_in: "Log In" logging_in: "Logging In" log_out: "Log Out" - recover: "recover account" + forgot_password: "Forgot your password?" authenticate_gplus: "Authenticate G+" load_profile: "Load G+ Profile" - load_email: "Load G+ Email" finishing: "Finishing" + sign_in_with_facebook: "Sign in with Facebook" + sign_in_with_gplus: "Sign in with G+" + signup_switch: "Want to create an account?" signup: - create_account_title: "Create Account to Save Progress" - description: "It's free. Just need a couple things and you'll be good to go:" email_announcements: "Receive announcements by email" - coppa: "13+ or non-USA " - coppa_why: "(Why?)" creating: "Creating Account..." sign_up: "Sign Up" log_in: "log in with password" social_signup: "Or, you can sign up through Facebook or G+:" required: "You need to log in before you can go that way." + login_switch: "Already have an account?" recover: recover_account_title: "Recover Account" @@ -128,6 +126,8 @@ books: "Books" common: + back: "Back" # When used as an action verb, like "Navigate backward" + continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Loading..." saving: "Saving..." sending: "Sending..." @@ -140,9 +140,14 @@ fork: "Fork" play: "Play" # When used as an action verb, like "Play next level" retry: "Retry" + actions: "Actions" + info: "Info" + help: "Help" watch: "Watch" unwatch: "Unwatch" submit_patch: "Submit Patch" + submit_changes: "Submit Changes" + save_changes: "Save Changes" general: and: "and" @@ -150,9 +155,22 @@ date: "Date" body: "Body" version: "Version" + pending: "Pending" + accepted: "Accepted" + rejected: "Rejected" + withdrawn: "Withdrawn" + submitter: "Submitter" + submitted: "Submitted" commit_msg: "Commit Message" + review: "Review" version_history: "Version History" version_history_for: "Version History for: " + select_changes: "Select two changes below to see the difference." + undo_prefix: "Undo" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Redo" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Play preview of current level" result: "Result" results: "Results" description: "Description" @@ -175,6 +193,9 @@ hard: "Hard" player: "Player" player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Warrior" + ranger: "Ranger" + wizard: "Wizard" units: second: "second" @@ -215,6 +236,7 @@ reload_title: "Reload All Code?" reload_really: "Are you sure you want to reload this level back to the beginning?" reload_confirm: "Reload All" + victory: "Victory" victory_title_prefix: "" victory_title_suffix: " Complete" victory_sign_up: "Sign Up to Save Progress" @@ -222,17 +244,17 @@ victory_rate_the_level: "Rate the level: " # Only in old-style levels. victory_return_to_ladder: "Return to Ladder" victory_play_continue: "Continue" - victory_play_skip: "Skip Ahead" - victory_play_next_level: "Play Next Level" - victory_play_more_practice: "More Practice" - victory_play_too_easy: "Too Easy" - victory_play_just_right: "Just Right" - victory_play_too_hard: "Too Hard" victory_saving_progress: "Saving Progress" - victory_go_home: "Go Home" # Only in old-style levels. - victory_review: "Tell us more!" # Only in old-style levels. + victory_go_home: "Go Home" + victory_review: "Tell us more!" + victory_review_placeholder: "How was the level?" victory_hour_of_code_done: "Are You Done?" victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" + victory_experience_gained: "XP Gained" + victory_gems_gained: "Gems Gained" + victory_new_item: "New Item" + victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." + victory_become_a_viking: "Become a Viking" guide_title: "Guide" tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -243,7 +265,7 @@ tome_submit_button: "Submit" tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. tome_select_method: "Select a Method" - tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Select Someone for " tome_available_spells: "Available Spells" tome_your_skills: "Your Skills" @@ -256,16 +278,23 @@ loading_ready: "Ready!" loading_start: "Start Level" problem_alert_title: "Fix Your Code" + problem_alert_help: "Help" time_current: "Now:" time_total: "Max:" time_goto: "Go to:" + non_user_code_problem_title: "Unable to Load Level" + infinite_loop_title: "Infinite Loop Detected" + infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." + check_dev_console: "You can also open the developer console to see what might be going wrong." + check_dev_console_link: "(instructions)" infinite_loop_try_again: "Try Again" infinite_loop_reset_level: "Reset Level" infinite_loop_comment_out: "Comment Out My Code" tip_toggle_play: "Toggle play/paused with Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." + tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." tip_open_source: "CodeCombat is 100% open source!" + tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat launched its beta in October, 2013." tip_think_solution: "Think of the solution, not the problem." tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -291,13 +320,33 @@ tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Customize Wizard" + tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." + tip_superpower: "Coding is the closest thing we have to a superpower." + tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" + tip_no_code: "No code is faster than no code." + tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" + tip_reusable_software: "Before software can be reusable it first has to be usable." + tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" + tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" + tip_source_code: "I want to change the world but they would not give me the source code." + tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" + tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." + tip_google: "Have a problem you can't solve? Google it!" + tip_adding_evil: "Adding a pinch of evil." + tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" + tip_open_source_contribute: "You can help CodeCombat improve!" + tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" + tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" + tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" + tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventory" save_load_tab: "Save/Load" options_tab: "Options" guide_tab: "Guide" + guide_video_tutorial: "Video Tutorial" + guide_tips: "Tips" multiplayer_tab: "Multiplayer" auth_tab: "Sign Up" inventory_caption: "Equip your hero" @@ -308,9 +357,24 @@ multiplayer_caption: "Play with friends!" auth_caption: "Save your progress." + leaderboard: + leaderboard: "Leaderboard" + view_other_solutions: "View Leaderboards" + scores: "Scores" + top_players: "Top Players by" + day: "Today" + week: "This Week" + all: "All-Time" + time: "Time" + damage_taken: "Damage Taken" + damage_dealt: "Damage Dealt" + difficulty: "Difficulty" + gold_collected: "Gold Collected" + inventory: choose_inventory: "Equip Items" equipped_item: "Equipped" + required_purchase_title: "Required" available_item: "Available" restricted_title: "Restricted" should_equip: "(double-click to equip)" @@ -330,6 +394,78 @@ prompt_title: "Not Enough Gems" prompt_body: "Do you want to get more?" prompt_button: "Enter Shop" + recovered: "Previous gems purchase recovered. Please refresh the page." + price: "x3500 / mo" + + subscribe: + comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" + feature1: "100+ basic levels across 4 worlds" + feature2: "10 powerful <strong>new heroes</strong> with unique skills!" + feature3: "70+ bonus levels" + feature4: "<strong>3500 bonus gems</strong> every month!" + feature5: "Video tutorials" + feature6: "Premium email support" + feature7: "Private <strong>Clans</strong>" + free: "Free" + month: "month" + subscribe_title: "Subscribe" + unsubscribe: "Unsubscribe" + confirm_unsubscribe: "Confirm Unsubscribe" + never_mind: "Never Mind, I Still Love You" + thank_you_months_prefix: "Thank you for supporting us these last" + thank_you_months_suffix: "months." + thank_you: "Thank you for supporting CodeCombat." + sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." + unsubscribe_feedback_placeholder: "O, what have we done?" + parent_button: "Ask your parent" + parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." + parent_email_input_invalid: "Email address invalid." + parent_email_input_label: "Parent email address" + parent_email_input_placeholder: "Enter parent email" + parent_email_send: "Send Email" + parent_email_sent: "Email sent!" + parent_email_title: "What's your parent's email?" + parents: "For Parents" + parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" + parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." + parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." + parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." + payment_methods: "Payment Methods" + payment_methods_title: "Accepted Payment Methods" + payment_methods_blurb1: "We currently accept credit cards and Alipay." + payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "Monthly Subscription" + subscription_required_to_play: "You'll need a subscription to play this level." + unlock_help_videos: "Subscribe to unlock all video tutorials." + + personal_sub: "Personal Subscription" # Accounts Subscription View below + loading_info: "Loading subscription information..." + managed_by: "Managed by" + will_be_cancelled: "Will be cancelled on" + currently_free: "You currently have a free subscription" + currently_free_until: "You currently have a free subscription until" + was_free_until: "You had a free subscription until" + managed_subs: "Managed Subscriptions" + managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" + managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." + group_discounts: "Group discounts" + group_discounts_1: "We also offer group discounts for bulk subscriptions." + group_discounts_1st: "1st subscription" + group_discounts_full: "Full price" + group_discounts_2nd: "Subscriptions 2-11" + group_discounts_20: "20% off" + group_discounts_12th: "Subscriptions 12+" + group_discounts_40: "40% off" + subscribing: "Subscribing..." + recipient_emails_placeholder: "Enter email address to subscribe, one per line." + subscribe_users: "Subscribe Users" + users_subscribed: "Users subscribed:" + no_users_subscribed: "No users subscribed, please double check your email addresses." + current_recipients: "Current Recipients" + unsubscribing: "Unsubscribing..." + subscribe_prepaid: "Click Subscribe to use prepaid code" + using_prepaid: "Using prepaid code for monthly subscription" choose_hero: choose_hero: "Choose Your Hero" @@ -344,6 +480,7 @@ lua_blurb: "Game scripting language." io_blurb: "Simple but obscure." status: "Status" + hero_type: "Type" weapons: "Weapons" weapons_warrior: "Swords - Short Range, No Magic" weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -354,9 +491,18 @@ regeneration: "Regeneration" range: "Range" # As in "attack or visual range" blocks: "Blocks" # As in "this shield blocks this much damage" + backstab: "Backstab" # As in "this dagger does this much backstab damage" skills: "Skills" - available_for_purchase: "Available for Purchase" - level_to_unlock: "Level to unlock:" + attack_1: "Deals" + attack_2: "of listed" + attack_3: "weapon damage." + health_1: "Gains" + health_2: "of listed" + health_3: "armor health." + speed_1: "Moves at" + speed_2: "meters per second." + available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) restricted_to_certain_heroes: "Only certain heroes can play this level." skill_docs: @@ -386,8 +532,6 @@ volume_label: "Volume" music_label: "Music" music_description: "Turn background music on/off." - autorun_label: "Autorun" - autorun_description: "Control automatic code execution." editor_config: "Editor Config" editor_config_title: "Editor Configuration" editor_config_level_language_label: "Language for This Level" @@ -420,34 +564,128 @@ press_paragraph_1_link: "press packet" press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." team: "Team" - george_title: "CEO" + george_title: "Cofounder" george_blurb: "Businesser" - scott_title: "Programmer" + scott_title: "Cofounder" scott_blurb: "Reasonable One" - nick_title: "Programmer" + nick_title: "Cofounder" nick_blurb: "Motivation Guru" michael_title: "Programmer" michael_blurb: "Sys Admin" - matt_title: "Programmer" + matt_title: "Cofounder" matt_blurb: "Bicyclist" + cat_title: "Chief Artisan" + cat_blurb: "Airbender" + josh_title: "Game Designer" + josh_blurb: "Floor Is Lava" + jose_title: "Music" + jose_blurb: "Taking Off" + retrostyle_title: "Illustration" + retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat: Info for Teachers" + intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." + intro_2: "No experience required!" + free_title: "How much does it cost?" + cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." + free_1: "There are 100+ FREE levels which cover every concept." + free_2: "A monthly subscription provides access to video tutorials and extra practice levels." + teacher_subs_title: "Teachers get free subscriptions!" + teacher_subs_1: "Please fill out our" + teacher_subs_2: "Teacher Survey" + teacher_subs_3: "to set up your subscription." + sub_includes_title: "What is included in the subscription?" + sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" + sub_includes_2: "70+ practice levels" + sub_includes_3: "Video tutorials" + sub_includes_4: "Premium email support" + sub_includes_5: "10 new heroes with unique skills to master" + sub_includes_6: "3500 bonus gems every month" + sub_includes_7: "Private Clans" + monitor_progress_title: "How do I monitor student progress?" + monitor_progress_1: "Student progress can be monitored by creating a" + monitor_progress_2: "for your class." + monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" + monitor_progress_4: "page." + monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." + private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." + private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" + private_clans_3: "." + who_for_title: "Who is CodeCombat for?" + who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." + who_for_2: "We've designed CodeCombat to appeal to both boys and girls." + material_title: "How much material is there?" + material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." + material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." + concepts_title: "What concepts are covered?" + how_much_title: "How much does a monthly subscription cost?" + how_much_1: "A" + how_much_2: "monthly subscription" + how_much_3: "costs $9.99, and can be cancelled anytime." + how_much_4: "Additionally, we provide discounts for larger groups:" + how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" + how_much_6: "for more details." + more_info_title: "Where can I find more information?" + more_info_1: "Our" + more_info_2: "teachers forum" + more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." + sys_requirements_title: "System Requirements" + sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." + sys_requirements_2: "CodeCombat is not supported on iPad yet." + + teachers_survey: + title: "Teacher Survey" + must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." + retrieving: "Retrieving information..." + being_reviewed_1: "Your application for a free trial subscription is being" + being_reviewed_2: "reviewed." + approved_1: "Your application for a free trial subscription was" + approved_2: "approved." + approved_3: "Further instructions have been sent to" + denied_1: "Your application for a free trial subscription has been" + denied_2: "denied." + contact_1: "Please contact" + contact_2: "if you have further questions." + description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" + description_2: "teachers" + description_3: "page." + description_4: "Please fill out this quick survey and we’ll email you setup instructions." + email: "Email Address" + school: "Name of School" + location: "Name of City" + age_students: "How old are your students?" + under: "Under" + other: "Other:" + amount_students: "How many students do you teach?" + hear_about: "How did you hear about CodeCombat?" + fill_fields: "Please fill out all fields." + thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Save New Version" new_major_version: "New Major Version" + submitting_patch: "Submitting Patch..." cla_prefix: "To save changes, first you must agree to our" cla_url: "CLA" cla_suffix: "." cla_agree: "I AGREE" + owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contact CodeCombat" welcome: "Good to hear from you! Use this form to send us email. " - contribute_prefix: "If you're interested in contributing, check out our " - contribute_page: "contribute page" - contribute_suffix: "!" forum_prefix: "For anything public, please try " forum_page: "our forum" forum_suffix: " instead." + faq_prefix: "There's also a" + faq: "FAQ" + subscribe_prefix: "If you need help figuring out a level, please" + subscribe: "buy a CodeCombat subscription" + subscribe_suffix: "and we'll be happy to help you with your code." + subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." + screenshot_included: "Screenshot included." + where_reply: "Where should we reply?" send: "Send Feedback" contact_candidate: "Contact Candidate" # Deprecated recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -458,12 +696,19 @@ autosave: "Changes Save Automatically" me_tab: "Me" picture_tab: "Picture" + delete_account_tab: "Delete Your Account" + wrong_email: "Wrong Email" + wrong_password: "Wrong Password" upload_picture: "Upload a picture" + delete_this_account: "Delete this account permanently" + god_mode: "God Mode" password_tab: "Password" emails_tab: "Emails" admin: "Admin" new_password: "New Password" new_password_verify: "Verify" + type_in_email: "Type in your email to confirm account deletion." + type_in_password: "Also, type in your password." email_subscriptions: "Email Subscriptions" email_subscriptions_none: "No Email Subscriptions." email_announcements: "Announcements" @@ -489,13 +734,12 @@ job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." sample_profile: "See a sample profile" view_profile: "View Your Profile" - wizard_tab: "Wizard" - wizard_color: "Wizard Clothes Color" keyboard_shortcuts: keyboard_shortcuts: "Keyboard Shortcuts" space: "Space" enter: "Enter" + press_enter: "press enter" escape: "Escape" shift: "Shift" run_code: "Run current code." @@ -511,7 +755,6 @@ toggle_pathfinding: "Toggle pathfinding overlay." beautify: "Beautify your code by standardizing its formatting." maximize_editor: "Maximize/minimize code editor." - move_wizard: "Move your Wizard around the level." community: main_title: "CodeCombat Community" @@ -531,19 +774,69 @@ social_hipchat: "Chat with us in the public CodeCombat HipChat room" contribute_to_the_project: "Contribute to the project" + clans: + clan: "Clan" + clans: "Clans" + new_name: "New clan name" + new_description: "New clan description" + make_private: "Make clan private" + subs_only: "subscribers only" + create_clan: "Create New Clan" + private_preview: "Preview" + public_clans: "Public Clans" + my_clans: "My Clans" + clan_name: "Clan Name" + name: "Name" + chieftain: "Chieftain" + type: "Type" + edit_clan_name: "Edit Clan Name" + edit_clan_description: "Edit Clan Description" + edit_name: "edit name" + edit_description: "edit description" + private: "(private)" + summary: "Summary" + average_level: "Average Level" + average_achievements: "Average Achievements" + delete_clan: "Delete Clan" + leave_clan: "Leave Clan" + join_clan: "Join Clan" + invite_1: "Invite:" + invite_2: "*Invite players to this Clan by sending them this link." + members: "Members" + progress: "Progress" + not_started_1: "not started" + started_1: "started" + complete_1: "complete" + exp_levels: "Expand levels" + rem_hero: "Remove Hero" + status: "Status" + complete_2: "Complete" + started_2: "Started" + not_started_2: "Not Started" + view_solution: "Click to view solution." + latest_achievement: "Latest Achievement" + playtime: "Playtime" + last_played: "Last played" + classes: archmage_title: "Archmage" archmage_title_description: "(Coder)" + archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "Artisan" artisan_title_description: "(Level Builder)" + artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "Adventurer" adventurer_title_description: "(Level Playtester)" + adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Scribe" scribe_title_description: "(Article Editor)" + scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "Diplomat" diplomat_title_description: "(Translator)" + diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "Ambassador" ambassador_title_description: "(Support)" + ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: main_title: "CodeCombat Editors" @@ -551,18 +844,28 @@ thang_title: "Thang Editor" level_title: "Level Editor" achievement_title: "Achievement Editor" + poll_title: "Poll Editor" back: "Back" revert: "Revert" revert_models: "Revert Models" pick_a_terrain: "Pick A Terrain" - small: "Small" + dungeon: "Dungeon" + indoor: "Indoor" + desert: "Desert" grassy: "Grassy" + mountain: "Mountain" + glacier: "Glacier" + small: "Small" + large: "Large" fork_title: "Fork New Version" fork_creating: "Creating Fork..." generate_terrain: "Generate Terrain" more: "More" wiki: "Wiki" live_chat: "Live Chat" + thang_main: "Main" + thang_spritesheets: "Spritesheets" + thang_colors: "Colors" level_some_options: "Some Options?" level_tab_thangs: "Thangs" level_tab_scripts: "Scripts" @@ -574,8 +877,13 @@ level_tab_thangs_all: "All" level_tab_thangs_conditions: "Starting Conditions" level_tab_thangs_add: "Add Thangs" + level_tab_thangs_search: "Search thangs" + add_components: "Add Components" + component_configs: "Component Configurations" + config_thang: "Double click to configure a thang" delete: "Delete" duplicate: "Duplicate" + stop_duplicate: "Stop Duplicate" rotate: "Rotate" level_settings_title: "Settings" level_component_tab_title: "Current Components" @@ -600,33 +908,36 @@ new_level_title_login: "Log In to Create a New Level" new_achievement_title: "Create a New Achievement" new_achievement_title_login: "Log In to Create a New Achievement" + new_poll_title: "Create a New Poll" + new_poll_title_login: "Log In to Create a New Poll" article_search_title: "Search Articles Here" thang_search_title: "Search Thang Types Here" level_search_title: "Search Levels Here" achievement_search_title: "Search Achievements" + poll_search_title: "Search Polls" read_only_warning2: "Note: you can't save any edits here, because you're not logged in." no_achievements: "No achievements have been added for this level yet." achievement_query_misc: "Key achievement off of miscellanea" achievement_query_goals: "Key achievement off of level goals" level_completion: "Level Completion" pop_i18n: "Populate I18N" + tasks: "Tasks" + clear_storage: "Clear your local changes" + add_system_title: "Add Systems to Level" + done_adding: "Done Adding" article: edit_btn_preview: "Preview" edit_article_title: "Edit Article" + polls: + priority: "Priority" + contribute: page_title: "Contributing" - character_classes_title: "Character Classes" - introduction_desc_intro: "We have high hopes for CodeCombat." - introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " - introduction_desc_github_url: "CodeCombat is totally open source" - introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." - introduction_desc_ending: "We hope you'll join our party!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" + intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" alert_account_message_intro: "Hey there!" alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." class_attributes: "Class Attributes" archmage_attribute_1_pref: "Knowledge in " @@ -639,10 +950,7 @@ join_desc_4: "and we'll go from there!" join_url_email: "Email us" join_url_hipchat: "public HipChat room" - more_about_archmage: "Learn More About Becoming an Archmage" archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." - artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" - artisan_summary_suf: ", then this class is for you." artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" artisan_introduction_suf: ", then this class might be for you." artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -653,28 +961,21 @@ artisan_join_step2: "Create a new level and explore existing levels." artisan_join_step3: "Find us in our public HipChat room for help." artisan_join_step4: "Post your levels on the forum for feedback." - more_about_artisan: "Learn More About Becoming an Artisan" artisan_subscribe_desc: "Get emails on level editor updates and announcements." - adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" adventurer_forum_url: "our forum" adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" - more_about_adventurer: "Learn More About Becoming an Adventurer" adventurer_subscribe_desc: "Get emails when there are new levels to test." - scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " - scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." contact_us_url: "Contact us" scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" - more_about_scribe: "Learn More About Becoming a Scribe" scribe_subscribe_desc: "Get emails about article writing announcements." - diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." diplomat_introduction_pref: "So, if there's one thing we learned from the " diplomat_launch_url: "launch in October" diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -685,15 +986,12 @@ diplomat_join_pref_github: "Find your language locale file " diplomat_github_url: "on GitHub" diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" - more_about_diplomat: "Learn More About Becoming a Diplomat" diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." - ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." - ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." + ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" ambassador_join_note_strong: "Note" ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" - more_about_ambassador: "Learn More About Becoming an Ambassador" ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." changes_auto_save: "Changes are saved automatically when you toggle checkboxes." diligent_scribes: "Our Diligent Scribes:" @@ -748,11 +1046,13 @@ fight: "Fight!" watch_victory: "Watch your victory" defeat_the: "Defeat the" + tournament_started: ", started" tournament_ends: "Tournament ends" tournament_ended: "Tournament ended" tournament_rules: "Tournament Rules" tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" + tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" tournament_blurb_blog: "on our blog" rules: "Rules" winners: "Winners" @@ -771,6 +1071,7 @@ no_achievements: "No Achievements earned yet." favorite_prefix: "Favorite language is " favorite_postfix: "." + not_member_of_clans: "Not a member of any clans yet." achievements: last_earned: "Last Earned" @@ -794,12 +1095,34 @@ recently_played: "Recently Played" no_recent_games: "No games played during the past two weeks." payments: "Payments" + purchased: "Purchased" + subscription: "Subscription" + invoices: "Invoices" service_apple: "Apple" service_web: "Web" paid_on: "Paid On" service: "Service" price: "Price" gems: "Gems" + active: "Active" + subscribed: "Subscribed" + unsubscribed: "Unsubscribed" + active_until: "Active Until" + cost: "Cost" + next_payment: "Next Payment" + card: "Card" + status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." + status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + + account_invoices: + amount: "Amount in US dollars" + declined: "Your card was declined" + invalid_amount: "Please enter a US dollar amount." + not_logged_in: "Log in or create an account to access invoices." + pay: "Pay Invoice" + purchasing: "Purchasing..." + retrying: "Server error, retrying." + success: "Successfully paid. Thanks!" loading_error: could_not_load: "Error loading from server" @@ -827,6 +1150,7 @@ leaderboard: "Leaderboard" user_schema: "User Schema" user_profile: "User Profile" + patch: "Patch" patches: "Patches" patched_model: "Source Document" model: "Model" @@ -853,16 +1177,43 @@ user_remarks: "User Remarks" versions: "Versions" items: "Items" + hero: "Hero" heroes: "Heroes" - wizard: "Wizard" achievement: "Achievement" clas: "CLAs" play_counts: "Play Counts" feedback: "Feedback" + payment_info: "Payment Info" + campaigns: "Campaigns" + poll: "Poll" + user_polls_record: "Poll Voting History" + + concepts: + advanced_strings: "Advanced Strings" + algorithms: "Algorithms" + arguments: "Arguments" + arithmetic: "Arithmetic" + arrays: "Arrays" + basic_syntax: "Basic Syntax" + boolean_logic: "Boolean Logic" + break_statements: "Break Statements" + classes: "Classes" + for_loops: "For Loops" + functions: "Functions" + if_statements: "If Statements" + input_handling: "Input Handling" + math_operations: "Math Operations" + object_literals: "Object Literals" + strings: "Strings" + variables: "Variables" + vectors: "Vectors" + while_loops: "Loops" + recursion: "Recursion" delta: added: "Added" modified: "Modified" + not_modified: "Not Modified" deleted: "Deleted" moved_index: "Moved Index" text_diff: "Text Diff" @@ -884,7 +1235,7 @@ legal: page_title: "Legal" - opensource_intro: "CodeCombat is free to play and completely open source." + opensource_intro: "CodeCombat is completely open source." opensource_description_prefix: "Check out " github_url: "our GitHub" opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -893,7 +1244,7 @@ practices_title: "Respectful Best Practices" practices_description: "These are our promises to you, the player, in slightly less legalese." privacy_title: "Privacy" - privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." + privacy_description: "We will not sell any of your personal information." security_title: "Security" security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." email_title: "Email" @@ -901,13 +1252,7 @@ email_settings_url: "your email settings" email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." cost_title: "Cost" - cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" - recruitment_title: "Recruitment" - recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." - url_hire_programmers: "No one can hire programmers fast enough" - recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" - recruitment_description_italic: "a lot" - recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." + cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." copyrights_title: "Copyrights and Licenses" contributor_title: "Contributor License Agreement" contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -941,7 +1286,7 @@ rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." nutshell_title: "In a Nutshell" nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." - canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." + canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." ladder_prizes: title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -963,22 +1308,6 @@ license: "license" oreilly: "ebook of your choice" - wizard_settings: - title: "Wizard Settings" - customize_avatar: "Customize Your Avatar" - active: "Active" - color: "Color" - group: "Group" - clothes: "Clothes" - trim: "Trim" - cloud: "Cloud" - team: "Team" - spell: "Spell" - boots: "Boots" - hue: "Hue" - saturation: "Saturation" - lightness: "Lightness" - account_profile: settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Edit Profile" diff --git a/app/locale/eo.coffee b/app/locale/eo.coffee new file mode 100644 index 000000000..5c371548f --- /dev/null +++ b/app/locale/eo.coffee @@ -0,0 +1,1478 @@ +module.exports = nativeDescription: "Esperanto", englishDescription: "Esperanto", translation: + home: + slogan: "Lernu programi per ludo" +# no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older +# no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices +# play: "Play" # The big play button that opens up the campaign view. +# old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari +# old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." +# campaign: "Campaign" +# for_beginners: "For Beginners" +# multiplayer: "Multiplayer" # Not currently shown on home page +# for_developers: "For Developers" # Not currently shown on home page. +# or_ipad: "Or download for iPad" + + nav: + play: "Niveloj" # The top nav bar entry where players choose which levels to play + community: "Komunumo" + editor: "Redaktoro" + blog: "Blogo" + forum: "Forumo" + account: "Konto" + profile: "Profilo" + stats: "Statistiko" + code: "Kodo" + admin: "Administrado" # Only shows up when you are an admin + home: "Hejmo" + contribute: "Kontribui" + legal: "Leĝa informo" + about: "Pri" + contact: "Kontakti" + twitter_follow: "Sekvu" + teachers: "Instruistoj" + + modal: + close: "Fermi" + okay: "Bone" + + not_found: + page_not_found: "Paĝo ne trovita" + + diplomat_suggestion: + title: "Helpu traduki CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. + sub_heading: "Ni bezonas vian lingvokapabloj." + pitch_body: "We develop CodeCombat in English, but we already have players all over the world. There's got to be at least one of them who wants to play in Esperanto! So if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Esperanto." + missing_translations: "Until we can translate everything into Esperanto, you'll see English when Esperanto isn't available." + learn_more: "Eksciu pli pri esti Diplomato" + subscribe_as_diplomat: "Aboni kiel Diplomato" + +# play: +# play_as: "Play As" # Ladder page +# spectate: "Spectate" # Ladder page +# players: "players" # Hover over a level on /play +# hours_played: "hours played" # Hover over a level on /play +# items: "Items" # Tooltip on item shop button from /play +# unlock: "Unlock" # For purchasing items and heroes +# confirm: "Confirm" +# owned: "Owned" # For items you own +# locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased +# available: "Available" +# skills_granted: "Skills Granted" # Property documentation details +# heroes: "Heroes" # Tooltip on hero shop button from /play +# achievements: "Achievements" # Tooltip on achievement list button from /play +# account: "Account" # Tooltip on account button from /play +# settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play +# next: "Next" # Go from choose hero to choose inventory before playing a level +# change_hero: "Change Hero" # Go back from choose inventory to choose hero +# choose_inventory: "Equip Items" +# buy_gems: "Buy Gems" +# subscription_required: "Subscription Required" +# anonymous: "Anonymous Player" +# level_difficulty: "Difficulty: " +# campaign_beginner: "Beginner Campaign" +# awaiting_levels_adventurer_prefix: "We release new levels every week." +# awaiting_levels_adventurer: "Sign up as an Adventurer" +# awaiting_levels_adventurer_suffix: "to be the first to play new levels." +# adjust_volume: "Adjust volume" +# campaign_multiplayer: "Multiplayer Arenas" +# campaign_multiplayer_description: "... in which you code head-to-head against other players." +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" + +# login: +# sign_up: "Create Account" +# log_in: "Log In" +# logging_in: "Logging In" +# log_out: "Log Out" +# forgot_password: "Forgot your password?" +# authenticate_gplus: "Authenticate G+" +# load_profile: "Load G+ Profile" +# finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" + +# signup: +# email_announcements: "Receive announcements by email" +# creating: "Creating Account..." +# sign_up: "Sign Up" +# log_in: "log in with password" +# social_signup: "Or, you can sign up through Facebook or G+:" +# required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" + +# recover: +# recover_account_title: "Recover Account" +# send_password: "Send Recovery Password" +# recovery_sent: "Recovery email sent." + +# items: +# primary: "Primary" +# secondary: "Secondary" +# armor: "Armor" +# accessories: "Accessories" +# misc: "Misc" +# books: "Books" + +# common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" +# loading: "Loading..." +# saving: "Saving..." +# sending: "Sending..." +# send: "Send" +# cancel: "Cancel" +# save: "Save" +# publish: "Publish" +# create: "Create" +# manual: "Manual" +# fork: "Fork" +# play: "Play" # When used as an action verb, like "Play next level" +# retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" +# watch: "Watch" +# unwatch: "Unwatch" +# submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" + +# general: +# and: "and" +# name: "Name" +# date: "Date" +# body: "Body" +# version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" +# commit_msg: "Commit Message" +# review: "Review" +# version_history: "Version History" +# version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" +# result: "Result" +# results: "Results" +# description: "Description" +# or: "or" +# subject: "Subject" +# email: "Email" +# password: "Password" +# message: "Message" +# code: "Code" +# ladder: "Ladder" +# when: "When" +# opponent: "Opponent" +# rank: "Rank" +# score: "Score" +# win: "Win" +# loss: "Loss" +# tie: "Tie" +# easy: "Easy" +# medium: "Medium" +# hard: "Hard" +# player: "Player" +# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" + +# units: +# second: "second" +# seconds: "seconds" +# minute: "minute" +# minutes: "minutes" +# hour: "hour" +# hours: "hours" +# day: "day" +# days: "days" +# week: "week" +# weeks: "weeks" +# month: "month" +# months: "months" +# year: "year" +# years: "years" + +# play_level: +# done: "Done" +# home: "Home" # Not used any more, will be removed soon. +# level: "Level" # Like "Level: Dungeons of Kithgard" +# skip: "Skip" +# game_menu: "Game Menu" +# guide: "Guide" +# restart: "Restart" +# goals: "Goals" +# goal: "Goal" +# running: "Running..." +# success: "Success!" +# incomplete: "Incomplete" +# timed_out: "Ran out of time" +# failing: "Failing" +# action_timeline: "Action Timeline" +# click_to_select: "Click on a unit to select it." +# control_bar_multiplayer: "Multiplayer" +# control_bar_join_game: "Join Game" +# reload: "Reload" +# reload_title: "Reload All Code?" +# reload_really: "Are you sure you want to reload this level back to the beginning?" +# reload_confirm: "Reload All" +# victory: "Victory" +# victory_title_prefix: "" +# victory_title_suffix: " Complete" +# victory_sign_up: "Sign Up to Save Progress" +# victory_sign_up_poke: "Want to save your code? Create a free account!" +# victory_rate_the_level: "Rate the level: " # Only in old-style levels. +# victory_return_to_ladder: "Return to Ladder" +# victory_play_continue: "Continue" +# victory_saving_progress: "Saving Progress" +# victory_go_home: "Go Home" # Only in old-style levels. +# victory_review: "Tell us more!" # Only in old-style levels. +# victory_hour_of_code_done: "Are You Done?" +# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" +# guide_title: "Guide" +# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. +# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. +# tome_other_units: "Other Units" # Only in old-style levels. +# tome_cast_button_run: "Run" +# tome_cast_button_running: "Running" +# tome_cast_button_ran: "Ran" +# tome_submit_button: "Submit" +# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. +# tome_select_method: "Select a Method" +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). +# tome_select_a_thang: "Select Someone for " +# tome_available_spells: "Available Spells" +# tome_your_skills: "Your Skills" +# tome_help: "Help" +# tome_current_method: "Current Method" +# hud_continue_short: "Continue" +# code_saved: "Code Saved" +# skip_tutorial: "Skip (esc)" +# keyboard_shortcuts: "Key Shortcuts" +# loading_ready: "Ready!" +# loading_start: "Start Level" +# problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" +# time_current: "Now:" +# time_total: "Max:" +# time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" +# infinite_loop_try_again: "Try Again" +# infinite_loop_reset_level: "Reset Level" +# infinite_loop_comment_out: "Comment Out My Code" +# tip_toggle_play: "Toggle play/paused with Ctrl+P." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." +# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." +# tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" +# tip_beta_launch: "CodeCombat launched its beta in October, 2013." +# tip_think_solution: "Think of the solution, not the problem." +# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" +# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" +# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" +# tip_forums: "Head over to the forums and tell us what you think!" +# tip_baby_coders: "In the future, even babies will be Archmages." +# tip_morale_improves: "Loading will continue until morale improves." +# tip_all_species: "We believe in equal opportunities to learn programming for all species." +# tip_reticulating: "Reticulating spines." +# tip_harry: "Yer a Wizard, " +# tip_great_responsibility: "With great coding skill comes great debug responsibility." +# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." +# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." +# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" +# tip_no_try: "Do. Or do not. There is no try. - Yoda" +# tip_patience: "Patience you must have, young Padawan. - Yoda" +# tip_documented_bug: "A documented bug is not a bug; it is a feature." +# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" +# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" +# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" +# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." +# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." +# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" +# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." + +# game_menu: +# inventory_tab: "Inventory" +# save_load_tab: "Save/Load" +# options_tab: "Options" +# guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" +# multiplayer_tab: "Multiplayer" +# auth_tab: "Sign Up" +# inventory_caption: "Equip your hero" +# choose_hero_caption: "Choose hero, language" +# save_load_caption: "... and view history" +# options_caption: "Configure settings" +# guide_caption: "Docs and tips" +# multiplayer_caption: "Play with friends!" +# auth_caption: "Save your progress." + +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + +# inventory: +# choose_inventory: "Equip Items" +# equipped_item: "Equipped" +# required_purchase_title: "Required" +# available_item: "Available" +# restricted_title: "Restricted" +# should_equip: "(double-click to equip)" +# equipped: "(equipped)" +# locked: "(locked)" +# restricted: "(restricted in this level)" +# equip: "Equip" +# unequip: "Unequip" + +# buy_gems: +# few_gems: "A few gems" +# pile_gems: "Pile of gems" +# chest_gems: "Chest of gems" +# purchasing: "Purchasing..." +# declined: "Your card was declined" +# retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" + +# choose_hero: +# choose_hero: "Choose Your Hero" +# programming_language: "Programming Language" +# programming_language_description: "Which programming language do you want to use?" +# default: "Default" +# experimental: "Experimental" +# python_blurb: "Simple yet powerful, great for beginners and experts." +# javascript_blurb: "The language of the web. (Not the same as Java.)" +# coffeescript_blurb: "Nicer JavaScript syntax." +# clojure_blurb: "A modern Lisp." +# lua_blurb: "Game scripting language." +# io_blurb: "Simple but obscure." +# status: "Status" +# hero_type: "Type" +# weapons: "Weapons" +# weapons_warrior: "Swords - Short Range, No Magic" +# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" +# weapons_wizard: "Wands, Staffs - Long Range, Magic" +# attack: "Damage" # Can also translate as "Attack" +# health: "Health" +# speed: "Speed" +# regeneration: "Regeneration" +# range: "Range" # As in "attack or visual range" +# blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" +# skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." + +# skill_docs: +# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this +# read_only: "read-only" +# action_name: "name" +# action_cooldown: "Takes" +# action_specific_cooldown: "Cooldown" +# action_damage: "Damage" +# action_range: "Range" +# action_radius: "Radius" +# action_duration: "Duration" +# example: "Example" +# ex: "ex" # Abbreviation of "example" +# current_value: "Current Value" +# default_value: "Default value" +# parameters: "Parameters" +# returns: "Returns" +# granted_by: "Granted by" + +# save_load: +# granularity_saved_games: "Saved" +# granularity_change_history: "History" + +# options: +# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level +# volume_label: "Volume" +# music_label: "Music" +# music_description: "Turn background music on/off." +# editor_config: "Editor Config" +# editor_config_title: "Editor Configuration" +# editor_config_level_language_label: "Language for This Level" +# editor_config_level_language_description: "Define the programming language for this particular level." +# editor_config_default_language_label: "Default Programming Language" +# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." +# editor_config_keybindings_label: "Key Bindings" +# editor_config_keybindings_default: "Default (Ace)" +# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." +# editor_config_livecompletion_label: "Live Autocompletion" +# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." +# editor_config_invisibles_label: "Show Invisibles" +# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." +# editor_config_indentguides_label: "Show Indent Guides" +# editor_config_indentguides_description: "Displays vertical lines to see indentation better." +# editor_config_behaviors_label: "Smart Behaviors" +# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." + +# about: +# why_codecombat: "Why CodeCombat?" +# why_paragraph_1: "If you want to learn to program, you don't need lessons. You need to write a lot of code and have a great time doing it." +# why_paragraph_2_prefix: "That's what programming is about. It's gotta be fun. Not fun like" +# why_paragraph_2_italic: "yay a badge" +# why_paragraph_2_center: "but fun like" +# why_paragraph_2_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" +# why_paragraph_2_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." +# why_paragraph_3: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." +# press_title: "Bloggers/Press" +# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" +# press_paragraph_1_link: "press packet" +# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." +# team: "Team" +# george_title: "Cofounder" +# george_blurb: "Businesser" +# scott_title: "Cofounder" +# scott_blurb: "Reasonable One" +# nick_title: "Cofounder" +# nick_blurb: "Motivation Guru" +# michael_title: "Programmer" +# michael_blurb: "Sys Admin" +# matt_title: "Cofounder" +# matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." + +# versions: +# save_version_title: "Save New Version" +# new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." +# cla_prefix: "To save changes, first you must agree to our" +# cla_url: "CLA" +# cla_suffix: "." +# cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." + +# contact: +# contact_us: "Contact CodeCombat" +# welcome: "Good to hear from you! Use this form to send us email. " +# forum_prefix: "For anything public, please try " +# forum_page: "our forum" +# forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" +# send: "Send Feedback" +# contact_candidate: "Contact Candidate" # Deprecated +# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated + +# account_settings: +# title: "Account Settings" +# not_logged_in: "Log in or create an account to change your settings." +# autosave: "Changes Save Automatically" +# me_tab: "Me" +# picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" +# upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" +# password_tab: "Password" +# emails_tab: "Emails" +# admin: "Admin" +# new_password: "New Password" +# new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." +# email_subscriptions: "Email Subscriptions" +# email_subscriptions_none: "No Email Subscriptions." +# email_announcements: "Announcements" +# email_announcements_description: "Get emails on the latest news and developments at CodeCombat." +# email_notifications: "Notifications" +# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." +# email_any_notes: "Any Notifications" +# email_any_notes_description: "Disable to stop all activity notification emails." +# email_news: "News" +# email_recruit_notes: "Job Opportunities" +# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." +# contributor_emails: "Contributor Class Emails" +# contribute_prefix: "We're looking for people to join our party! Check out the " +# contribute_page: "contribute page" +# contribute_suffix: " to find out more." +# email_toggle: "Toggle All" +# error_saving: "Error Saving" +# saved: "Changes Saved" +# password_mismatch: "Password does not match." +# password_repeat: "Please repeat your password." +# job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated +# job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." +# job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." +# sample_profile: "See a sample profile" +# view_profile: "View Your Profile" + +# keyboard_shortcuts: +# keyboard_shortcuts: "Keyboard Shortcuts" +# space: "Space" +# enter: "Enter" +# press_enter: "press enter" +# escape: "Escape" +# shift: "Shift" +# run_code: "Run current code." +# run_real_time: "Run in real time." +# continue_script: "Continue past current script." +# skip_scripts: "Skip past all skippable scripts." +# toggle_playback: "Toggle play/pause." +# scrub_playback: "Scrub back and forward through time." +# single_scrub_playback: "Scrub back and forward through time by a single frame." +# scrub_execution: "Scrub through current spell execution." +# toggle_debug: "Toggle debug display." +# toggle_grid: "Toggle grid overlay." +# toggle_pathfinding: "Toggle pathfinding overlay." +# beautify: "Beautify your code by standardizing its formatting." +# maximize_editor: "Maximize/minimize code editor." + +# community: +# main_title: "CodeCombat Community" +# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" +# level_editor_prefix: "Use the CodeCombat" +# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" +# thang_editor_prefix: "We call units within the game 'thangs'. Use the" +# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." +# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" +# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." +# find_us: "Find us on these sites" +# social_blog: "Read the CodeCombat blog on Sett" +# social_discource: "Join the discussion on our Discourse forum" +# social_facebook: "Like CodeCombat on Facebook" +# social_twitter: "Follow CodeCombat on Twitter" +# social_gplus: "Join CodeCombat on Google+" +# social_hipchat: "Chat with us in the public CodeCombat HipChat room" +# contribute_to_the_project: "Contribute to the project" + +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + +# classes: +# archmage_title: "Archmage" +# archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" +# artisan_title: "Artisan" +# artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." +# adventurer_title: "Adventurer" +# adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." +# scribe_title: "Scribe" +# scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." +# diplomat_title: "Diplomat" +# diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." +# ambassador_title: "Ambassador" +# ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." + +# editor: +# main_title: "CodeCombat Editors" +# article_title: "Article Editor" +# thang_title: "Thang Editor" +# level_title: "Level Editor" +# achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" +# back: "Back" +# revert: "Revert" +# revert_models: "Revert Models" +# pick_a_terrain: "Pick A Terrain" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" +# grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" +# fork_title: "Fork New Version" +# fork_creating: "Creating Fork..." +# generate_terrain: "Generate Terrain" +# more: "More" +# wiki: "Wiki" +# live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" +# level_some_options: "Some Options?" +# level_tab_thangs: "Thangs" +# level_tab_scripts: "Scripts" +# level_tab_settings: "Settings" +# level_tab_components: "Components" +# level_tab_systems: "Systems" +# level_tab_docs: "Documentation" +# level_tab_thangs_title: "Current Thangs" +# level_tab_thangs_all: "All" +# level_tab_thangs_conditions: "Starting Conditions" +# level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" +# delete: "Delete" +# duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" +# rotate: "Rotate" +# level_settings_title: "Settings" +# level_component_tab_title: "Current Components" +# level_component_btn_new: "Create New Component" +# level_systems_tab_title: "Current Systems" +# level_systems_btn_new: "Create New System" +# level_systems_btn_add: "Add System" +# level_components_title: "Back to All Thangs" +# level_components_type: "Type" +# level_component_edit_title: "Edit Component" +# level_component_config_schema: "Config Schema" +# level_component_settings: "Settings" +# level_system_edit_title: "Edit System" +# create_system_title: "Create New System" +# new_component_title: "Create New Component" +# new_component_field_system: "System" +# new_article_title: "Create a New Article" +# new_thang_title: "Create a New Thang Type" +# new_level_title: "Create a New Level" +# new_article_title_login: "Log In to Create a New Article" +# new_thang_title_login: "Log In to Create a New Thang Type" +# new_level_title_login: "Log In to Create a New Level" +# new_achievement_title: "Create a New Achievement" +# new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" +# article_search_title: "Search Articles Here" +# thang_search_title: "Search Thang Types Here" +# level_search_title: "Search Levels Here" +# achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" +# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." +# no_achievements: "No achievements have been added for this level yet." +# achievement_query_misc: "Key achievement off of miscellanea" +# achievement_query_goals: "Key achievement off of level goals" +# level_completion: "Level Completion" +# pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" + +# article: +# edit_btn_preview: "Preview" +# edit_article_title: "Edit Article" + +# polls: +# priority: "Priority" + +# contribute: +# page_title: "Contributing" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" +# alert_account_message_intro: "Hey there!" +# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." +# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." +# class_attributes: "Class Attributes" +# archmage_attribute_1_pref: "Knowledge in " +# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." +# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." +# how_to_join: "How To Join" +# join_desc_1: "Anyone can help out! Just check out our " +# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " +# join_desc_3: ", or find us in our " +# join_desc_4: "and we'll go from there!" +# join_url_email: "Email us" +# join_url_hipchat: "public HipChat room" +# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." +# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" +# artisan_introduction_suf: ", then this class might be for you." +# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" +# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." +# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" +# artisan_join_desc: "Use the Level Editor in these steps, give or take:" +# artisan_join_step1: "Read the documentation." +# artisan_join_step2: "Create a new level and explore existing levels." +# artisan_join_step3: "Find us in our public HipChat room for help." +# artisan_join_step4: "Post your levels on the forum for feedback." +# artisan_subscribe_desc: "Get emails on level editor updates and announcements." +# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." +# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." +# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." +# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" +# adventurer_forum_url: "our forum" +# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" +# adventurer_subscribe_desc: "Get emails when there are new levels to test." +# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " +# scribe_introduction_url_mozilla: "Mozilla Developer Network" +# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." +# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." +# contact_us_url: "Contact us" +# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" +# scribe_subscribe_desc: "Get emails about article writing announcements." +# diplomat_introduction_pref: "So, if there's one thing we learned from the " +# diplomat_launch_url: "launch in October" +# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." +# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" +# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" +# diplomat_i18n_page: "translations page" +# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." +# diplomat_join_pref_github: "Find your language locale file " +# diplomat_github_url: "on GitHub" +# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" +# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" +# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" +# ambassador_join_note_strong: "Note" +# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" +# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." +# changes_auto_save: "Changes are saved automatically when you toggle checkboxes." +# diligent_scribes: "Our Diligent Scribes:" +# powerful_archmages: "Our Powerful Archmages:" +# creative_artisans: "Our Creative Artisans:" +# brave_adventurers: "Our Brave Adventurers:" +# translating_diplomats: "Our Translating Diplomats:" +# helpful_ambassadors: "Our Helpful Ambassadors:" + +# ladder: +# please_login: "Please log in first before playing a ladder game." +# my_matches: "My Matches" +# simulate: "Simulate" +# simulation_explanation: "By simulating games you can get your game ranked faster!" +# simulate_games: "Simulate Games!" +# simulate_all: "RESET AND SIMULATE GAMES" +# games_simulated_by: "Games simulated by you:" +# games_simulated_for: "Games simulated for you:" +# games_simulated: "Games simulated" +# games_played: "Games played" +# ratio: "Ratio" +# leaderboard: "Leaderboard" +# battle_as: "Battle as " +# summary_your: "Your " +# summary_matches: "Matches - " +# summary_wins: " Wins, " +# summary_losses: " Losses" +# rank_no_code: "No New Code to Rank" +# rank_my_game: "Rank My Game!" +# rank_submitting: "Submitting..." +# rank_submitted: "Submitted for Ranking" +# rank_failed: "Failed to Rank" +# rank_being_ranked: "Game Being Ranked" +# rank_last_submitted: "submitted " +# help_simulate: "Help simulate games?" +# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." +# no_ranked_matches_pre: "No ranked matches for the " +# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." +# choose_opponent: "Choose an Opponent" +# select_your_language: "Select your language!" +# tutorial_play: "Play Tutorial" +# tutorial_recommended: "Recommended if you've never played before" +# tutorial_skip: "Skip Tutorial" +# tutorial_not_sure: "Not sure what's going on?" +# tutorial_play_first: "Play the Tutorial first." +# simple_ai: "Simple AI" +# warmup: "Warmup" +# friends_playing: "Friends Playing" +# log_in_for_friends: "Log in to play with your friends!" +# social_connect_blurb: "Connect and play against your friends!" +# invite_friends_to_battle: "Invite your friends to join you in battle!" +# fight: "Fight!" +# watch_victory: "Watch your victory" +# defeat_the: "Defeat the" +# tournament_started: ", started" +# tournament_ends: "Tournament ends" +# tournament_ended: "Tournament ended" +# tournament_rules: "Tournament Rules" +# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" +# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" +# tournament_blurb_blog: "on our blog" +# rules: "Rules" +# winners: "Winners" + +# user: +# stats: "Stats" +# singleplayer_title: "Singleplayer Levels" +# multiplayer_title: "Multiplayer Levels" +# achievements_title: "Achievements" +# last_played: "Last Played" +# status: "Status" +# status_completed: "Completed" +# status_unfinished: "Unfinished" +# no_singleplayer: "No Singleplayer games played yet." +# no_multiplayer: "No Multiplayer games played yet." +# no_achievements: "No Achievements earned yet." +# favorite_prefix: "Favorite language is " +# favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." + +# achievements: +# last_earned: "Last Earned" +# amount_achieved: "Amount" +# achievement: "Achievement" +# category_contributor: "Contributor" +# category_ladder: "Ladder" +# category_level: "Level" +# category_miscellaneous: "Miscellaneous" +# category_levels: "Levels" +# category_undefined: "Uncategorized" +# current_xp_prefix: "" +# current_xp_postfix: " in total" +# new_xp_prefix: "" +# new_xp_postfix: " earned" +# left_xp_prefix: "" +# left_xp_infix: " until level " +# left_xp_postfix: "" + +# account: +# recently_played: "Recently Played" +# no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" + +# loading_error: +# could_not_load: "Error loading from server" +# connection_failure: "Connection failed." +# unauthorized: "You need to be signed in. Do you have cookies disabled?" +# forbidden: "You do not have the permissions." +# not_found: "Not found." +# not_allowed: "Method not allowed." +# timeout: "Server timeout." +# conflict: "Resource conflict." +# bad_input: "Bad input." +# server_error: "Server error." +# unknown: "Unknown error." + +# resources: +# sessions: "Sessions" +# your_sessions: "Your Sessions" +# level: "Level" +# social_network_apis: "Social Network APIs" +# facebook_status: "Facebook Status" +# facebook_friends: "Facebook Friends" +# facebook_friend_sessions: "Facebook Friend Sessions" +# gplus_friends: "G+ Friends" +# gplus_friend_sessions: "G+ Friend Sessions" +# leaderboard: "Leaderboard" +# user_schema: "User Schema" +# user_profile: "User Profile" +# patch: "Patch" +# patches: "Patches" +# patched_model: "Source Document" +# model: "Model" +# system: "System" +# systems: "Systems" +# component: "Component" +# components: "Components" +# thang: "Thang" +# thangs: "Thangs" +# level_session: "Your Session" +# opponent_session: "Opponent Session" +# article: "Article" +# user_names: "User Names" +# thang_names: "Thang Names" +# files: "Files" +# top_simulators: "Top Simulators" +# source_document: "Source Document" +# document: "Document" +# sprite_sheet: "Sprite Sheet" +# employers: "Employers" +# candidates: "Candidates" +# candidate_sessions: "Candidate Sessions" +# user_remark: "User Remark" +# user_remarks: "User Remarks" +# versions: "Versions" +# items: "Items" +# hero: "Hero" +# heroes: "Heroes" +# achievement: "Achievement" +# clas: "CLAs" +# play_counts: "Play Counts" +# feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" + +# delta: +# added: "Added" +# modified: "Modified" +# not_modified: "Not Modified" +# deleted: "Deleted" +# moved_index: "Moved Index" +# text_diff: "Text Diff" +# merge_conflict_with: "MERGE CONFLICT WITH" +# no_changes: "No Changes" + +# guide: +# temp: "Temp" + +# multiplayer: +# multiplayer_title: "Multiplayer Settings" # We'll be changing this around significantly soon. Until then, it's not important to translate. +# multiplayer_toggle: "Enable multiplayer" +# multiplayer_toggle_description: "Allow others to join your game." +# multiplayer_link_description: "Give this link to anyone to have them join you." +# multiplayer_hint_label: "Hint:" +# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." +# multiplayer_coming_soon: "More multiplayer features to come!" +# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." + +# legal: +# page_title: "Legal" +# opensource_intro: "CodeCombat is completely open source." +# opensource_description_prefix: "Check out " +# github_url: "our GitHub" +# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " +# archmage_wiki_url: "our Archmage wiki" +# opensource_description_suffix: "for a list of the software that makes this game possible." +# practices_title: "Respectful Best Practices" +# practices_description: "These are our promises to you, the player, in slightly less legalese." +# privacy_title: "Privacy" +# privacy_description: "We will not sell any of your personal information." +# security_title: "Security" +# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." +# email_title: "Email" +# email_description_prefix: "We will not inundate you with spam. Through" +# email_settings_url: "your email settings" +# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." +# cost_title: "Cost" +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." +# copyrights_title: "Copyrights and Licenses" +# contributor_title: "Contributor License Agreement" +# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" +# cla_url: "CLA" +# contributor_description_suffix: "to which you should agree before contributing." +# code_title: "Code - MIT" +# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" +# mit_license_url: "MIT license" +# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." +# art_title: "Art/Music - Creative Commons " +# art_description_prefix: "All common content is available under the" +# cc_license_url: "Creative Commons Attribution 4.0 International License" +# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" +# art_music: "Music" +# art_sound: "Sound" +# art_artwork: "Artwork" +# art_sprites: "Sprites" +# art_other: "Any and all other non-code creative works that are made available when creating Levels." +# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." +# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" +# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." +# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." +# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." +# rights_title: "Rights Reserved" +# rights_desc: "All rights are reserved for Levels themselves. This includes" +# rights_scripts: "Scripts" +# rights_unit: "Unit configuration" +# rights_description: "Description" +# rights_writings: "Writings" +# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." +# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." +# nutshell_title: "In a Nutshell" +# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." + +# ladder_prizes: +# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. +# blurb_1: "These prizes will be awarded according to" +# blurb_2: "the tournament rules" +# blurb_3: "to the top human and ogre players." +# blurb_4: "Two teams means double the prizes!" +# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" +# rank: "Rank" +# prizes: "Prizes" +# total_value: "Total Value" +# in_cash: "in cash" +# custom_wizard: "Custom CodeCombat Wizard" +# custom_avatar: "Custom CodeCombat avatar" +# heap: "for six months of \"Startup\" access" +# credits: "credits" +# one_month_coupon: "coupon: choose either Rails or HTML" +# one_month_discount: "discount, 30% off: choose either Rails or HTML" +# license: "license" +# oreilly: "ebook of your choice" + +# account_profile: +# settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. +# edit_profile: "Edit Profile" +# done_editing: "Done Editing" +# profile_for_prefix: "Profile for " +# profile_for_suffix: "" +# featured: "Featured" +# not_featured: "Not Featured" +# looking_for: "Looking for:" +# last_updated: "Last updated:" +# contact: "Contact" +# active: "Looking for interview offers now" +# inactive: "Not looking for offers right now" +# complete: "complete" +# next: "Next" +# next_city: "city?" +# next_country: "pick your country." +# next_name: "name?" +# next_short_description: "write a short description." +# next_long_description: "describe your desired position." +# next_skills: "list at least five skills." +# next_work: "chronicle your work history." +# next_education: "recount your educational ordeals." +# next_projects: "show off up to three projects you've worked on." +# next_links: "add any personal or social links." +# next_photo: "add an optional professional photo." +# next_active: "mark yourself open to offers to show up in searches." +# example_blog: "Blog" +# example_personal_site: "Personal Site" +# links_header: "Personal Links" +# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." +# links_name: "Link Name" +# links_name_help: "What are you linking to?" +# links_link_blurb: "Link URL" +# basics_header: "Update basic info" +# basics_active: "Open to Offers" +# basics_active_help: "Want interview offers right now?" +# basics_job_title: "Desired Job Title" +# basics_job_title_help: "What role are you looking for?" +# basics_city: "City" +# basics_city_help: "City you want to work in (or live in now)." +# basics_country: "Country" +# basics_country_help: "Country you want to work in (or live in now)." +# basics_visa: "US Work Status" +# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" +# basics_looking_for: "Looking For" +# basics_looking_for_full_time: "Full-time" +# basics_looking_for_part_time: "Part-time" +# basics_looking_for_remote: "Remote" +# basics_looking_for_contracting: "Contracting" +# basics_looking_for_internship: "Internship" +# basics_looking_for_help: "What kind of developer position do you want?" +# name_header: "Fill in your name" +# name_anonymous: "Anonymous Developer" +# name_help: "Name you want employers to see, like 'Nick Winter'." +# short_description_header: "Write a short description of yourself" +# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." +# short_description: "Tagline" +# short_description_help: "Who are you, and what are you looking for? 140 characters max." +# skills_header: "Skills" +# skills_help: "Tag relevant developer skills in order of proficiency." +# long_description_header: "Describe your desired position" +# long_description_blurb: "Tell employers how awesome you are and what role you want." +# long_description: "Self Description" +# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." +# work_experience: "Work Experience" +# work_header: "Chronicle your work history" +# work_years: "Years of Experience" +# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" +# work_blurb: "List your relevant work experience, most recent first." +# work_employer: "Employer" +# work_employer_help: "Name of your employer." +# work_role: "Job Title" +# work_role_help: "What was your job title or role?" +# work_duration: "Duration" +# work_duration_help: "When did you hold this gig?" +# work_description: "Description" +# work_description_help: "What did you do there? (140 chars; optional)" +# education: "Education" +# education_header: "Recount your academic ordeals" +# education_blurb: "List your academic ordeals." +# education_school: "School" +# education_school_help: "Name of your school." +# education_degree: "Degree" +# education_degree_help: "What was your degree and field of study?" +# education_duration: "Dates" +# education_duration_help: "When?" +# education_description: "Description" +# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" +# our_notes: "CodeCombat's Notes" +# remarks: "Remarks" +# projects: "Projects" +# projects_header: "Add 3 projects" +# projects_header_2: "Projects (Top 3)" +# projects_blurb: "Highlight your projects to amaze employers." +# project_name: "Project Name" +# project_name_help: "What was the project called?" +# project_description: "Description" +# project_description_help: "Briefly describe the project." +# project_picture: "Picture" +# project_picture_help: "Upload a 230x115px or larger image showing off the project." +# project_link: "Link" +# project_link_help: "Link to the project." +# player_code: "Player Code" + +# employers: +# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." +# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." +# hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. +# get_started: "Get Started" +# already_screened: "We've already technically screened all our candidates" +# filter_further: ", but you can also filter further:" +# filter_visa: "Visa" +# filter_visa_yes: "US Authorized" +# filter_visa_no: "Not Authorized" +# filter_education_top: "Top School" +# filter_education_other: "Other" +# filter_role_web_developer: "Web Developer" +# filter_role_software_developer: "Software Developer" +# filter_role_mobile_developer: "Mobile Developer" +# filter_experience: "Experience" +# filter_experience_senior: "Senior" +# filter_experience_junior: "Junior" +# filter_experience_recent_grad: "Recent Grad" +# filter_experience_student: "College Student" +# filter_results: "results" +# start_hiring: "Start hiring." +# reasons: "Three reasons you should hire through us:" +# everyone_looking: "Everyone here is looking for their next opportunity." +# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." +# weeding: "Sit back; we've done the weeding for you." +# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." +# pass_screen: "They will pass your technical screen." +# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." +# make_hiring_easier: "Make my hiring easier, please." +# what: "What is CodeCombat?" +# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." +# cost: "How much do we charge?" +# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." +# candidate_name: "Name" +# candidate_location: "Location" +# candidate_looking_for: "Looking For" +# candidate_role: "Role" +# candidate_top_skills: "Top Skills" +# candidate_years_experience: "Yrs Exp" +# candidate_last_updated: "Last Updated" +# candidate_who: "Who" +# featured_developers: "Featured Developers" +# other_developers: "Other Developers" +# inactive_developers: "Inactive Developers" + +# admin: +# av_espionage: "Espionage" # Really not important to translate /admin controls. +# av_espionage_placeholder: "Email or username" +# av_usersearch: "User Search" +# av_usersearch_placeholder: "Email, username, name, whatever" +# av_usersearch_search: "Search" +# av_title: "Admin Views" +# av_entities_sub_title: "Entities" +# av_entities_users_url: "Users" +# av_entities_active_instances_url: "Active Instances" +# av_entities_employer_list_url: "Employer List" +# av_entities_candidates_list_url: "Candidate List" +# av_entities_user_code_problems_list_url: "User Code Problems List" +# av_other_sub_title: "Other" +# av_other_debug_base_url: "Base (for debugging base.jade)" +# u_title: "User List" +# ucp_title: "User Code Problems" +# lg_title: "Latest Games" +# clas: "CLAs" diff --git a/app/locale/es-419.coffee b/app/locale/es-419.coffee index c10af814c..40272fbc4 100644 --- a/app/locale/es-419.coffee +++ b/app/locale/es-419.coffee @@ -1,16 +1,17 @@ -module.exports = nativeDescription: "español (América Latina)", englishDescription: "Spanish (Latin America)", translation: +module.exports = nativeDescription: "Español (América Latina)", englishDescription: "Spanish (Latin America)", translation: home: slogan: "Aprende a programar jugando" no_ie: "¡Lo sentimos! CodeCombat no funciona en Internet Explorer 8 o versiones anteriores." # Warning that only shows up in IE8 and older no_mobile: "¡CodeCombat no fue diseñado para dispositivos móviles y quizás no funcione!" # Warning that shows up on mobile devices - play: "Jugar" # The big play button that just starts playing a level - old_browser: "¡Oh! ¡Oh! Tu navegador es muy antiguo para correr CodeCombat. ¡Lo Sentimos!" # Warning that shows up on really old Firefox/Chrome/Safari + play: "Jugar" # The big play button that opens up the campaign view. + old_browser: "¡Oh! ¡Oh! Tu navegador es muy antiguo para correr CodeCombat. ¡Lo sentimos!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Puedes probar de todas formas, pero probablemente no funcione." + ipad_browser: "Malas noticias: CodeCombat no funciona en el navegador de iPad. Buenas noticias: nuestra propia aplicación de iPad esta en espera para ser aprobada por Apple." campaign: "Campaña" for_beginners: "Para Principiantes" multiplayer: "Multijugador" # Not currently shown on home page for_developers: "Para Desarrolladores" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "O descargar para iPad" nav: play: "Jugar" # The top nav bar entry where players choose which levels to play @@ -20,16 +21,16 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip forum: "Foro" account: "Cuenta" profile: "Perfil" - stats: "Stadísticas" - code: "Cógigo" + stats: "Estadísticas" + code: "Código" admin: "Admin" # Only shows up when you are an admin home: "Inicio" contribute: "Contribuir" legal: "Legal" - about: "Sobre" + about: "Acerca" contact: "Contacto" twitter_follow: "Seguir" - teachers: "Profesores" + teachers: "Maestros" modal: close: "Cerrar" @@ -41,7 +42,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip diplomat_suggestion: title: "¡Ayuda a traducir CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "Necesitamos tus habilidades de idioma." - pitch_body: "Desarrollamos CodeCombat en inglés, pero ya tenemos jugadores por todo el mundo. Muchos de ellos quieren jugar en español pero no hablan inglés, así que si puedes hablar ambos, por favor considera registrarte pare ser un Diplomático y ayudar a traducir tanto el sitio de CodeCombat como todos los niveles al español." + pitch_body: "Desarrollamos CodeCombat en inglés, pero ya tenemos jugadores de todo el mundo. Muchos de ellos quieren jugar en español pero no hablan inglés, así que si puedes hablar ambos, por favor considera registrarte pare ser un Diplomático y ayudar a traducir tanto el sitio de CodeCombat como todos los niveles al español." missing_translations: "Hasta que podamos traducir todo al español, verás inglés cuando el español no esté disponible." learn_more: "Aprende más sobre ser un Diplomático" subscribe_as_diplomat: "Suscribete como un Diplomático" @@ -49,84 +50,84 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip play: play_as: "Jugar Como " # Ladder page spectate: "Observar" # Ladder page - players: "jugadores" # Hover over a level on /play + players: "Jugadores" # Hover over a level on /play hours_played: "horas jugadas" # Hover over a level on /play items: "Objetos" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details + unlock: "Desbloquear" # For purchasing items and heroes + confirm: "Confirmar" + owned: "Adquirido" # For items you own + locked: "Bloqueado" + purchasable: "Adquirible" # For a hero you unlocked but haven't purchased + available: "Disponible" + skills_granted: "Habilidades concedidas" # Property documentation details heroes: "Héroes" # Tooltip on hero shop button from /play achievements: "Logros" # Tooltip on achievement list button from /play account: "Cuenta" # Tooltip on account button from /play settings: "Configuración" # Tooltip on settings button from /play + poll: "Encuestas" # Tooltip on poll button from /play next: "Próximo" # Go from choose hero to choose inventory before playing a level change_hero: "Cambiar héroe" # Go back from choose inventory to choose hero choose_inventory: "Equipar objetos" -# buy_gems: "Buy Gems" - older_campaigns: "Campañas previas" + buy_gems: "Comprar gemas" + subscription_required: "Requiere Suscripción" anonymous: "Jugador Anónimo" level_difficulty: "Dificultad: " campaign_beginner: "Campaña para principiantes" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Elige tu nivel" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Puedes saltar a cualquier nivel de abajo, o discutir los niveles en " - adventurer_forum: "el foro del aventurero" - adventurer_suffix: "." - campaign_old_beginner: "Campaña anterior de principiante" - campaign_old_beginner_description: "... en la que aprendes la hechicería de la programación." - campaign_dev: "Niveles aleatorios más difíciles" - campaign_dev_description: "... en los que aprendes sobre la interfaz mientras haces algo un poco más difícil." + awaiting_levels_adventurer_prefix: "Nosotros creamos 5 nuevos niveles cada semana" # {change} + awaiting_levels_adventurer: "Registrate como un aventurero" + awaiting_levels_adventurer_suffix: "para ser el primero en jugar nuevos niveles." + adjust_volume: "Ajustar el volumen" campaign_multiplayer: "Arenas Multijugador" - campaign_multiplayer_description: "... en las que programas cara-a-cara contra otros jugadores." - campaign_player_created: "Creados-Por-Jugadores" - campaign_player_created_description: "... en los que luchas contra la creatividad de tus compañeros <a href=\"/contribute#artisan\">Hechiceros Artesanales</a>." - campaign_classic_algorithms: "Algorítmos Clásicos" - campaign_classic_algorithms_description: "... en la cual aprendes los algorítmos más populares en las Ciencias de la Computación." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" + campaign_multiplayer_description: "... en las que programas cara a cara contra otros jugadores." +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "¡Estás haciendo un gran progreso! Cuéntale a alguien cuánto has aprendido con CodeCombat." # {change} + email_invalid: "Dirección de Email inválida." + form_blurb: "¡Ingresa su email debajo y les enseñaremos!" + form_label: "Dirección de Email" + placeholder: "dirección de email" + title: "Excelente Trabajo, Aprendiz" login: sign_up: "Crear Cuenta" log_in: "Entrar" logging_in: "Entrando" log_out: "Salir" - recover: "recuperar cuenta" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "¿Olvidaste la contraseña?" + authenticate_gplus: "Ingresar G+" + load_profile: "Cargar Perfil G+ " + finishing: "Finalizando" + sign_in_with_facebook: "Registrarse con Facebook" + sign_in_with_gplus: "Registrarse con G+" + signup_switch: "¿Quieres crear una cuenta?" signup: - create_account_title: "Crear Cuenta para Guardar el Progreso" - description: "Es gratis. Solo necesitas un par de cosas y estarás listo para comenzar:" email_announcements: "Recibe noticias por email" - coppa: "más de 13 años o fuera de los Estados Unidos" - coppa_why: "(¿Por qué?)" creating: "Creando Cuenta..." sign_up: "Registrarse" log_in: "Inicia sesión con tu contraseña" - social_signup: "O, puedes conectarte a través de Facebook o G+:" + social_signup: "O puedes conectarte a través de Facebook o G+:" required: "Necesitas entrar a tu cuenta antes de continuar." + login_switch: "¿Ya tienes una cuenta?" recover: recover_account_title: "recuperar cuenta" - send_password: "Enviar Contraseña de Recuperación" + send_password: "Enviar contraseña de recuperación" recovery_sent: "Correo de recuperación enviado." items: -# primary: "Primary" -# secondary: "Secondary" + primary: "Primario" + secondary: "Secundario" armor: "Armadura" accessories: "Accesorios" misc: "Misc" -# books: "Books" + books: "Libros" common: + back: "Volver" # When used as an action verb, like "Navigate backward" + continue: "Continuar" # When used as an action verb, like "Continue forward" loading: "Cargando..." saving: "Guardando..." sending: "Enviando..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip fork: "Bifurcar" play: "Jugar" # When used as an action verb, like "Play next level" retry: "Reintentar" + actions: "Acciones" + info: "Info" + help: "Ayuda" watch: "Seguir" unwatch: "No seguir" submit_patch: "Enviar Parche" + submit_changes: "Enviar cambios" + save_changes: "Guardar cambios" general: and: "y" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip date: "Fecha" body: "Cuerpo" version: "Versión" + pending: "Pendiente" + accepted: "Aceptado" + rejected: "Rechazado" + withdrawn: "Retirado" + submitter: "Emisor" + submitted: "Enviado" commit_msg: "Enviar mensaje" + review: "Revisión" version_history: "Historial de Versiones" version_history_for: "Historial de Versiones para: " + select_changes: "Selcciona dos cambios abajo para ver la diferencia" + undo_prefix: "Deshacer" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Rehacer" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Mira el avance del nivel" result: "Resultado" results: "Resultados" description: "Descripción" @@ -173,7 +192,10 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip medium: "Medio" hard: "Difícil" player: "Jugador" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Nivel" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Guerrero" + ranger: "Guardabosques" + wizard: "Mago" units: second: "segundo" @@ -194,44 +216,44 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip play_level: done: "Listo" home: "Inicio" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" + level: "Nivel" # Like "Level: Dungeons of Kithgard" skip: "Omitir" - game_menu: "Menu del Juego" - guide: "Guia" + game_menu: "Menú del Juego" + guide: "Guía" restart: "Reiniciar" goals: "Objetivos" goal: "Objetivo" -# running: "Running..." + running: "Andando..." success: "¡Éxito!" incomplete: "Incompleto" timed_out: "Se te acabo el tiempo" failing: "Fallando" - action_timeline: "Cronologia de Accion" + action_timeline: "Cronología de Acción" click_to_select: "Has click en una unidad para seleccionarla." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "Multijugador" + control_bar_join_game: "Ingresar al juego" + reload: "Recargar" reload_title: "¿Recargar Todo el Código?" reload_really: "¿Estás seguro de que quieres empezar este nivel desde el principio?" reload_confirm: "Recargar Todo" + victory: "Victoria" victory_title_prefix: "¡" victory_title_suffix: " Completo!" - victory_sign_up: "Registrate para recibir actualizaciones" + victory_sign_up: "Regístrate para recibir actualizaciones" victory_sign_up_poke: "¿Quieres recibir las ultimas noticias por correo? ¡Crea una cuenta gratuita y te mantendremos informado!" victory_rate_the_level: "Valora el nivel: " # Only in old-style levels. victory_return_to_ladder: "Volver a la escalera" victory_play_continue: "Continuar" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Jugar Próximo Nivel" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_saving_progress: "Guardando Progreso" victory_go_home: "Ir al Inicio" # Only in old-style levels. victory_review: "¡Cuéntanos más!" # Only in old-style levels. victory_hour_of_code_done: "¿Has acabado?" victory_hour_of_code_done_yes: "¡Si, he terminado con mi Hora de Código!" + victory_experience_gained: "XP Ganada" + victory_gems_gained: "Gemas Ganadas" + victory_new_item: "Objeto Nuevo" + victory_viking_code_school: "Santo cielo, Holy smokes, el nivel que acabas de pasar era dificil! Si todavía no eres un desarrollador de software, deberías serlo. Acabas de conseguir una aceptación por vía rápida con la Escuela Vikinga de Có, donde tú puedes llevar tus habilidades al siguiente nivel y convertirteen un desarrollador web profesional en 14 semanas." + victory_become_a_viking: "Conviértete en un Vikingo" guide_title: "Guía" tome_minion_spells: "Hechizos de tus Secuaces" # Only in old-style levels. tome_read_only_spells: "Hechizos de Sólo Lectura" # Only in old-style levels. @@ -242,28 +264,36 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip tome_submit_button: "Enviar" tome_reload_method: "Recargar código original para este método" # Title text for individual method reload button. tome_select_method: "Seleccionar un Método" - tome_see_all_methods: "Ver todos los métodos que puedes editar" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "Ver todos los métodos que puedes editar" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Selecciona Alguien para " tome_available_spells: "Hechizos Disponibles" tome_your_skills: "Tus habilidades" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_help: "Ayuda" + tome_current_method: "Método actual" + hud_continue_short: "Continuar" + code_saved: "Código Guardado" skip_tutorial: "Saltar (esc)" keyboard_shortcuts: "Atajos de teclado" loading_ready: "¡Listo!" loading_start: "Iniciar nivel" -# problem_alert_title: "Fix Your Code" + problem_alert_title: "Revisa tu código" + problem_alert_help: "Ayuda" time_current: "Ahora:" time_total: "Max:" time_goto: "Ir a:" + non_user_code_problem_title: "No se puede cargar el nivel" + infinite_loop_title: "Loop infinito detectado" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." + check_dev_console: "Tú puedes también abrir la consola de desarrollo para ver que puede salir mal." + check_dev_console_link: "(instrucciones)" infinite_loop_try_again: "Intentar nuevamente" infinite_loop_reset_level: "Reiniciar Nivel" infinite_loop_comment_out: "Comente Mi Código" tip_toggle_play: "Activa jugar/pausa con Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ y Ctrl+] rebobina y avance rápido." - tip_guide_exists: "Clique la guía en la parte superior de la página para obtener información útil" + tip_scrub_shortcut: "Ctrl+[ y Ctrl+] rebobina y avance rápido." # {change} + tip_guide_exists: "Haga click en la guía en la parte superior de la página para obtener información útil" tip_open_source: "¡CodeCombat es 100% código abierto!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat lanzó su beta en Octubre del 2013." tip_think_solution: "Piensa en la solución, no en el problema." tip_theory_practice: "En teoría, no hay diferencia entre la teoría y la práctica. Pero en la práctica, si la hay. - Yogi Berra" @@ -281,96 +311,218 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip tip_commitment_yoda: "Un programador debe tener el compromiso más profundo, la mente más seria. ~ Yoda" tip_no_try: "Haz. O no hagas. No hay intento. - Yoda" tip_patience: "Paciencia debes tener, joven Padawan. - Yoda" - tip_documented_bug: "Un error documentad no es un error; es una característica." + tip_documented_bug: "Un error documentado no es un error; es una característica." tip_impossible: "Siempre parece imposible hasta que se hace. - Nelson Mandela" - tip_talk_is_cheap: "Hablar es barato. Muestrame el código. - Linus Torvalds" - tip_first_language: "La cosa más desastroza que puedes aprender es tu primer lenguaje de programación. - Alan Kay" + tip_talk_is_cheap: "Hablar es barato. Muéstrame el código. - Linus Torvalds" + tip_first_language: "La cosa más desastrosa que puedes aprender es tu primer lenguaje de programación. - Alan Kay" tip_hardware_problem: "P: ¿Cuántos programadores son necesarios para cambiar una bombilla eléctrica? R: Ninguno, es un problema de hardware." - tip_hofstadters_law: "Ley de Hofstadter: Siempre toma más tiempo del que esperas, inclso cuando tienes en cuenta la ley de Hofstadter." + tip_hofstadters_law: "Ley de Hofstadter: Siempre toma más tiempo del que esperas, incluso cuando tienes en cuenta la ley de Hofstadter." tip_premature_optimization: "La optimización prematura es la raíz de la maldad. - Donald Knuth" tip_brute_force: "Cuando tengas duda, usa la fuerza bruta. - Ken Thompson" - customize_wizard: "Personalizar Hechicero" + tip_extrapolation: "Solo hay dos tipos de personas: Esas que pueden extrapolar desde información incompleta..." + tip_superpower: "Programar es lo más cercano que tenemos a superpoderes." + tip_control_destiny: "En el código abierto real, tú tienes el derecho de controlar tu propio destino. - Linus Torvalds" + tip_no_code: "Ningún código es más rápido que nada de código." + tip_code_never_lies: "El código nunca miente, los comentarios a veces sí. — Ron Jeffries" + tip_reusable_software: "Antes de que el software sea reusable, primero tiene que ser usable." + tip_optimization_operator: "Cada lenguaje tiene un operador de optimización. En la mayoría de ellos, ese operador es ‘//’" + tip_lines_of_code: "Medir el progreso en la programación en líneas de código es como medir el progreso de construcción de una aeronave por su peso. — Bill Gates" + tip_source_code: "Quisiera cambiar el mundo, pero no me dan el código fuente." + tip_javascript_java: "Java es a Javascript lo mismo que Comer es a Comercial. - Chris Heilmann" + tip_move_forward: "Hagas lo que hagas, siempre sigue hacia delante. - Martin Luther King Jr." + tip_google: "¿Tienes un problema que no puedes resolver? ¡Googléalo!" + tip_adding_evil: "Agregando una pizca de maldad." + tip_hate_computers: "Esa es la razón por la cual la gente piensa que odia las computadoras. Lo que ellos odian de verdad, es los pesimos programadores. - Larry Niven" + tip_open_source_contribute: "¡Tú puedes ayudar a CodeCombat a mejorar!" + tip_recurse: "Iterar es humano, recursar es divino. - L. Peter Deutsch" + tip_free_your_mind: "Tienes que dejar ir todo, Neo. Miedo, duda, e incredulidad. Libera tu mente. - Morpheus" + tip_strong_opponents: "Hasta los oponentes mas fuertes siempre tienen una debilidad. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventario" save_load_tab: "Guardar/Cargar" options_tab: "Opciones" guide_tab: "Guía" + guide_video_tutorial: "Guía en video" + guide_tips: "Pistas" multiplayer_tab: "Multijugador" -# auth_tab: "Sign Up" - inventory_caption: "Equipar tu héroe" + auth_tab: "Ingresar" + inventory_caption: "Equipar a tu héroe" choose_hero_caption: "Elegir héroe, lenguaje" save_load_caption: "... y ver historia" options_caption: "Hacer ajustes" guide_caption: "Documentos y consejos" multiplayer_caption: "¡Jugar con amigos!" -# auth_caption: "Save your progress." + auth_caption: "Guarda tu progreso." + + leaderboard: + leaderboard: "Tabla de Posiciones" + view_other_solutions: "Ver Otras Soluciones" # {change} + scores: "Puntajes" + top_players: "Mejores jugadores" + day: "Hoy" + week: "Esta Semana" + all: "Siempre" + time: "Tiempo" + damage_taken: "Daño Recibido" + damage_dealt: "Daño Infligido" + difficulty: "Dificultad" + gold_collected: "Oro Recolectado" inventory: - choose_inventory: "Elegir artículos" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + choose_inventory: "Equipar objetos" + equipped_item: "Equipado" + required_purchase_title: "Requerido" + available_item: "Disponible" + restricted_title: "Restringido" + should_equip: "(doble-click para equipar)" + equipped: "(equipado)" + locked: "(bloqueado)" + restricted: "(restringido en este nivel)" + equip: "Equipar" + unequip: "Sacar" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + buy_gems: + few_gems: "Pocas gemas" + pile_gems: "Pila de gemas" + chest_gems: "Cofre de gemas" + purchasing: "Comprando..." + declined: "Su tarjeta fue rechazada" + retrying: "Error del servidor, recargando." + prompt_title: "Gemas insuficientes" + prompt_body: "¿Quieres obtener más?" + prompt_button: "Entrar al mercado" + recovered: "Se recuperaron las anteriores compras de gemas. Por favor recarga la página" + price: "x3500 / mes" + + subscribe: + comparison_blurb: "Agudiza tus habilidades con la suscripción a CodeCombat!" + feature1: "Más de 60 niveles basicos a lo largo de 4 mundos" # {change} + feature2: "7 poderosos <strong>nuevos heroés</strong> con habilidades unicas!" # {change} + feature3: "Más de 30 niveles extras" # {change} + feature4: "<strong>3500 gemas bonus</strong> cada mes!" + feature5: "Video tutoriales" + feature6: "Soporte Premium vía email" + feature7: "<strong>Clan</strong> Privado" + free: "Gratis" + month: "mes" + subscribe_title: "Suscribirse" + unsubscribe: "Des-suscribirse" + confirm_unsubscribe: "Confirmar cancelacion de suscripción" + never_mind: "Olvidalo, Te sigo queriendo" + thank_you_months_prefix: "Gracias por tu apoyo en estos ultimos" + thank_you_months_suffix: "meses." + thank_you: "Gracias por apoyar CodeCombat." + sorry_to_see_you_go: "¡Sentimos que te vayas! Por favor, haznos saber lo que podríamos haber hecho mejor." + unsubscribe_feedback_placeholder: "¿Pero qué hemos hecho?" + parent_button: "Preguntale a tus padres" + parent_email_description: "Nosotros les mandaremos un email a ellos, así pueden comprarte la suscripción a CodeCombat." + parent_email_input_invalid: "Dirección de email invalida." + parent_email_input_label: "Dirección email padres" + parent_email_input_placeholder: "Ingresa el email de tus padres" + parent_email_send: "Enviar email" + parent_email_sent: "Email enviado!" + parent_email_title: "Cuál es el email de tus padres?" + parents: "Para padres" + parents_title: "Su hijo aprenderá a programar." # {change} + parents_blurb1: "Con CodeCombat, su hijo aprenderá a escribiendo código real. Empezaran aprendiendo comandos simples avanzando a temas más complejos." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "Por $9.99 USD/mes, recibirán nuevos desafíos todas las semanas y soporte personal por email de programadores profesionales." # {change} + parents_blurb3: "Sin Riesgo: Garantía de 100% de devolución, fácil 1-click y des- suscribirse." + payment_methods: "Metodos de pago" + payment_methods_title: "Metodos de pago aceptados." + payment_methods_blurb1: "Actualmente aceptamos tarjetas de credito y Alipay." + payment_methods_blurb2: "Si necesitas una forma alternativa de pago, por favor contactarse" + stripe_description: "Suscripción Mensual" + subscription_required_to_play: "Necesitas una suscripción para jugar este nivel." + unlock_help_videos: "Suscríbete para desbloquear todos los video tutoriales." + personal_sub: "Suscripción Personal" # Accounts Subscription View below + loading_info: "Cargando información de suscripción..." + managed_by: "Administrado por" + will_be_cancelled: "Será cancelado en" + currently_free: "Actualmente tienes una suscripción gratuita" + currently_free_until: "Actualmente tienes una suscripción gratuita hasta" + was_free_until: "Tuviste una suscripción gratuita hasta" + managed_subs: "Suscripciones administradas" + managed_subs_desc: "Agregar suscripciones para otros jugadores (alumnos, hijos, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." + group_discounts: "Descuentos por grupo" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." + group_discounts_1st: "1ra suscripción (incluye la tuya)" # {change} + group_discounts_full: "Precio regular" + group_discounts_2nd: "2-11 Suscripciones" + group_discounts_20: "20% descuento" + group_discounts_12th: "12+ Suscripciones" + group_discounts_40: "40% descuento" + subscribing: "Suscribiendo..." + recipient_emails_placeholder: "Ingresa las direcciones de email a suscribir, una por línea." + subscribe_users: "Suscribir Usuarios" + users_subscribed: "Usuarios suscritos:" + no_users_subscribed: "No se suscribieron usuarios, por favor revisa las direcciones de email." + current_recipients: "Recipientes actuales" + unsubscribing: "Desuscribiendo..." + subscribe_prepaid: "Click en suscribirse para utlizar un código prepago" + using_prepaid: "Usar código prepago para una suscribción mensual" choose_hero: choose_hero: "Elige tu héroe" programming_language: "Lenguaje de programación" programming_language_description: "¿Qué lenguaje de programación vas a elegir?" -# default: "Default" -# experimental: "Experimental" + default: "por Defecto" + experimental: "Experimental" python_blurb: "Simple pero poderoso." - javascript_blurb: "El lenguaje de la web." - coffeescript_blurb: "Mejor JavaScript." + javascript_blurb: "El lenguaje de la web (no es Java)." + coffeescript_blurb: "JavaScript pero más bonito." clojure_blurb: "Un Lisp moderno." - lua_blurb: "Para Juegos." + lua_blurb: "Lenguaje para Juegos." io_blurb: "Simple pero oscuro." status: "Estado" + hero_type: "Tipo" weapons: "Armas" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" + weapons_warrior: "Espada - Corto Alcance, Sin Magia" + weapons_ranger: "Ballestas, Armas - Largo Alcance, Sin Magia" + weapons_wizard: "Barita, - Largo Alcance, Mágico" + attack: "Daño" # Can also translate as "Attack" health: "Salud" speed: "Velocidad" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + regeneration: "Regeneración" + range: "Rango" # As in "attack or visual range" + blocks: "Bloqueo" # As in "this shield blocks this much damage" + backstab: "Apuñala" # As in "this dagger does this much backstab damage" + skills: "Habilidades" + attack_1: "Ofertas" + attack_2: "de la lista" + attack_3: "daño de arma." + health_1: "Gana" + health_2: "de la lista" + health_3: "salud de la armadura." + speed_1: "Se mueve a" + speed_2: "metros por segundo." + available_for_purchase: "Disponible para Comprar" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Nivel para desbloquear:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Sólo ciertos héroes pueden jugar este nivel." -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + skill_docs: + writable: "escribible" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "Sólo Lectura" + action_name: "nombre" + action_cooldown: "Toma" + action_specific_cooldown: "Enfriamiento" + action_damage: "Daño" + action_range: "Rango" + action_radius: "Radio" + action_duration: "Duración" + example: "Ejemplo" + ex: "ej" # Abbreviation of "example" + current_value: "Valor actual" + default_value: "Valor por defecto" + parameters: "Parámetros" + returns: "Devoluciones" + granted_by: "Concedido por" save_load: - granularity_saved_games: "Almacenado" + granularity_saved_games: "Guardado" granularity_change_history: "Historia" options: @@ -378,14 +530,12 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip volume_label: "Volumen" music_label: "Música" music_description: "Música encendida/apagada." - autorun_label: "Autoejecutar" - autorun_description: "Controlar ejecución automática de código." editor_config: "Config. de Editor" editor_config_title: "Configuración del Editor" editor_config_level_language_label: "Lenguaje para este Nivel" - editor_config_level_language_description: "Definir el lenguaje de programación para Este nivel." + editor_config_level_language_description: "Definir el lenguaje de programación para este nivel." editor_config_default_language_label: "Lenguaje de Programación Predeterminado" - editor_config_default_language_description: "Definir el lenguaje de programación que deseas para codificar cuando inicias nuevos niveles." + editor_config_default_language_description: "Definir el lenguaje de programación que deseas para programar cuando inicias nuevos niveles." editor_config_keybindings_label: "Atajos de Teclado" editor_config_keybindings_default: "Default (As)" editor_config_keybindings_description: "Añade atajos adicionales conocidos de los editores comunes." @@ -404,45 +554,139 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip why_paragraph_2_prefix: "De eso se trata la programación. Será divertido. No casi divertido" why_paragraph_2_italic: "bien un premio" why_paragraph_2_center: "pero algo divertido" - why_paragraph_2_italic_caps: "¡NO MAMÁ, TENGO QUE FINALIZAR EL NIVEL!" - why_paragraph_2_suffix: "Por tal motivo CodeCombat es un juego multiusuario, no un curso con gamificación. No finalizaremos hasta que terminos--pero en esta ocasión, es una buena cosa." - why_paragraph_3: "si te vas a volver adicto a un juego, hazlo a este y conviértete en un de los magos de la era tecnológica." + why_paragraph_2_italic_caps: "¡NO MAMÁ, TENGO QUE TERMINAR EL NIVEL!" + why_paragraph_2_suffix: "Por tal motivo CodeCombat es un juego multiusuario, no un curso con gamificación. No finalizaremos hasta que terminemos--pero en esta ocasión, es una buena cosa." + why_paragraph_3: "si te vas a volver adicto a un juego, que sea este y conviértete en uno de los magos de la era tecnológica." press_title: "Blogeros/Prensa" press_paragraph_1_prefix: "¿Quieres escribirnos? Descarga y usa con confianza todos los recursos incluídos en nuestro" press_paragraph_1_link: "paquete de prensa" press_paragraph_1_suffix: ". Todos los logos e imágenes pueden ser usados sin contactarnos directamente." team: "Equipo" - george_title: "CEO" + george_title: "CEO" # {change} george_blurb: "Negociante" - scott_title: "Programador" + scott_title: "Programador" # {change} scott_blurb: "Razonable" - nick_title: "Programador" + nick_title: "Programador" # {change} nick_blurb: "Gurú motivacional" michael_title: "Programador" michael_blurb: "Sys Admin" - matt_title: "Programador" + matt_title: "Programador" # {change} matt_blurb: "Bicicletero" + cat_title: "Jefe Artesano" + cat_blurb: "Maestro del Aire" + josh_title: "Diseñador de Juegos" + josh_blurb: "El piso es Lava" + jose_title: "Música" + jose_blurb: "Despegar" + retrostyle_title: "Ilustración" + retrostyle_blurb: "Juegos con estilo Retro" + + teachers: + title: "CodeCombat para Profesores" + intro_1: "CodeCombat es un juego online que enseña a programar.Los estudiantes escriben código en idiomas de programación real." + intro_2: "No se necesita experiencia previa!" + free_title: "¿Cuanto cuesta?" + cost_china: "CodeCombat es gratis en China por los primeros cinco niveles, despues cuesta $9.99(dólares) por mes para tener acceso a 120+ niveles que son exclusivos en nuestros servidores en China." # {change} + free_1: "CodeCombat Basic es GRATIS! Hay 70+ niveles gratis que cubren cada concepto." # {change} + free_2: "Una suscripción mensual le da acceso a tutoriales en vídeo y niveles extra para practicar." + teacher_subs_title: "¡Los amestros obtienen subscripciones gratuitas!" + teacher_subs_1: "Por favor contacte" # {change} + teacher_subs_2: "para configurar una suscripción mensual gratis." # {change} +# teacher_subs_3: "to set up your subscription." + sub_includes_title: "¿Qué se incluye en la suscripción?" + sub_includes_1: "Adicionalmente a los más de 70 niveles básicos, los estudiantes con una suscripción mensual obtienen acceso a estas características adicionales:" # {change} + sub_includes_2: "Más de 40 niveles de práctica" # {change} + sub_includes_3: "Video tutoriales" + sub_includes_4: "Soporte de correo electronico Premium" + sub_includes_5: "7 heroes nuevos con habilidades unicas que dominar" # {change} + sub_includes_6: "bonificación de 3500 gemas cada mes" + sub_includes_7: "Clanes privados" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." + who_for_title: "¿Para quienes es CodeCombat?" + who_for_1: "Recomendamos CodeCombat para estudiantes de edades 9 y arriba. No se require experiencia en programación." + who_for_2: "Hemos diseñado a CodeCombat para atraer a niños y niñas." + material_title: "Cuánto material hay?" + material_china: "Aproximadamente 22 horas de juego repartidas en más de 120 niveles sólo para suscriptores, con cinco nueveos niveles cada semana." # {change} + material_1: "Aproximadamente 8 horas de contenido gratis y un adicional de 14 horas de contenido de suscriptores, con cinco nueveos niveles cada semana." # {change} +# concepts_title: "What concepts are covered?" + how_much_title: "¿Cuánto cuesta una subscripción mensual?" + how_much_1: "una" + how_much_2: "suscribción mensual" + how_much_3: "Cuesta u$s9.99, y puede ser cancelada en cualquier momento." + how_much_4: "Adicionalmente, nosotros otorgamos descuentos a grupos grandes:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" + more_info_2: "el foro de profesores" + more_info_3: "es un buen lugar para connectarse con los educadores que estan usando CodeCombat." + sys_requirements_title: "Requerimientos del sistema" + sys_requirements_1: "Debido que CodeCombat es un juego, es más difícil para las computadoras correrlo en relación a un tutorial escrito o un video. Para que todos puedan jugar, hemos optimizado la web para correr rápidamente en todos los navegadores modernos y en maquinas antiguas. Dicho esto, aquí están nuestras sugerencias para sacar el máximo provecho de su experiencia en la Hora del Código:" # {change} + sys_requirements_2: "Usar una versión actualizada del navegador Chrome o Firefox." # {change} + + teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" + approved_2: "Aprobada." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" + denied_2: "denegadae." + contact_1: "Porfavor contactarse" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." + email: "Dirección de email" + school: "Nombre del colegio" + location: "Nombre de la ciudad" + age_students: "¿Qué edad tienen tus estudiantes?" +# under: "Under" +# other: "Other:" + amount_students: "¿A cuantos alumnos les enseñas?" + hear_about: "¿Donde escuchaste sobre CodeCombat?" + fill_fields: "Porfavor llenar todos los campos." + thanks: "Gracias! Vamos a mandarte instrucciónes para iniciar proximamente." versions: save_version_title: "Guardar nueva versión" new_major_version: "Nueva Gran Versión" + submitting_patch: "Publicando Parche..." cla_prefix: "Para guardar los cambios, primero debes estar de acuerdo con nuestro" cla_url: "CLA" cla_suffix: "." cla_agree: "ACEPTO" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contacta a CodeCombat" - welcome: "¡Qué bueno es escucharte! Usa este formulario para enviarnos un mensaje" - contribute_prefix: "¡Si estas interesado en contribuir, chequea nuestra " - contribute_page: "página de contribución" - contribute_suffix: "!" + welcome: "¡Qué bueno es escuchar algo de ti! Usa este formulario para enviarnos un mensaje" forum_prefix: "Para cualquier cosa pública, por favor prueba " forum_page: "nuestro foro " forum_suffix: "en su lugar." + faq_prefix: "También hay un" + faq: "FAQ" + subscribe_prefix: "Si necesitas ayuda para resolver un nivel, por favor" + subscribe: "compra una suscripción de CodeCombat" + subscribe_suffix: "y nosotros estaremos felices de ayudarte con tu código." + subscriber_support: "Como estás suscrito a CodeCombat, tu email tendrá prioridad." + screenshot_included: "Captura de pantalla incluida." + where_reply: "¿A dónde deberíamos responder?" send: "Enviar Comentario" contact_candidate: "Contacta un Candidato" # Deprecated - recruitment_reminder: "Usa este formulario para llegar a los candidadtos que estés interesado en entrevistar. Recuerda que CodeCombat cobra 18% del primer año de salario. Este honorario se debe a la contratación del empleado y reembolsable por 90 days si el empleado no permanece contratado. Tiempo partcial, remoto, y empleados por contrato son gratiso, como así también internos." # Deprecated + recruitment_reminder: "Usa este formulario para llegar a los candidatos que estés interesado en entrevistar. Recuerda que CodeCombat cobra 18% del primer año de salario. Este honorario se debe a la contratación del empleado y reembolsable por 90 días si el empleado no permanece contratado. Tiempo parcial, remoto, y empleados por contrato son gratis, como así también internos." # Deprecated account_settings: title: "Configuración de la Cuenta" @@ -450,14 +694,21 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip autosave: "Cambios Guardados Automáticamente" me_tab: "Yo" picture_tab: "Imagen" + delete_account_tab: "Borra tu cuenta" + wrong_email: "Mail Incorrecto" + wrong_password: "Contraseña incorrecta" upload_picture: "Sube una imagen" + delete_this_account: "Borrar esta cuenta permanentemente" + god_mode: "Modo Dios" password_tab: "Contraseña" emails_tab: "Correos" admin: "Admin" new_password: "Nueva Contraseña" new_password_verify: "Verificar" + type_in_email: "Ingrese su correo electrónico para confirmar la eliminación" # {change} +# type_in_password: "Also, type in your password." email_subscriptions: "Suscripciones de Email" - email_subscriptions_none: "No tienes subcripciones." + email_subscriptions_none: "No tienes suscripciones." email_announcements: "Noticias" email_announcements_description: "Recibe correos electrónicos con las últimas noticias y desarrollos de CodeCombat." email_notifications: "Notificaciones" @@ -481,219 +732,272 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip job_profile_explanation: "¡Hola! Llena esto, y te contactaremos acerca de encontrar un trabajo como desarrollador de software." sample_profile: "Mira un perfil de ejemplo" view_profile: "Ver tu perfil" - wizard_tab: "Hechicero" - wizard_color: "Color de Ropas del Hechicero" keyboard_shortcuts: - keyboard_shortcuts: "Keyboard Shortcuts" + keyboard_shortcuts: "Atajos de teclado" space: "Barra espaciadora" enter: "Enter" + press_enter: "Toca enter" escape: "Escape" shift: "Shift" -# run_code: "Run current code." + run_code: "Ejecutar el código." run_real_time: "Ejecutar en tiempo real." continue_script: "Continuar hasta finalizado el script." skip_scripts: "Omitir todos los scripts omitibles." toggle_playback: "Aplicar ejecutar/pausar." scrub_playback: "Devolverse y avanzar en el tiempo." single_scrub_playback: "Devolverse y avanzar en el tiempo de a un cuadro." -# scrub_execution: "Scrub through current spell execution." - toggle_debug: "Mostrar ocultar depuración." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." + scrub_execution: "Scrub through la ejecución del hechizo actual." + toggle_debug: "Mostrar/ocultar depuración." + toggle_grid: "Mostrar/ocultar rejilla." + toggle_pathfinding: "Mostrar/ocultar buscador de rutas." beautify: "Hacer bello tu código estandarizando formato." maximize_editor: "Maximizar/minimizar editor de código." - move_wizard: "Mover tu hechizero en el nivel." community: main_title: "Comunidad CodeCombat" introduction: "Mira las maneras en las que puedes involucrarte adelante y decide qué es más divertido. ¡Queremos trabajar contigo!" level_editor_prefix: "Usar CodeCombat" level_editor_suffix: "para crear y editar niveles. Los han creado niveles para sus clases, amigos, hackatones, estudiantes, familiares. Si crear un nuevo juego luce intimidante puedes ¡comenzar con base en uno nuestro!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + thang_editor_prefix: "Nosotros llamamos a las unidades del juego 'thangs'. Usa el" + thang_editor_suffix: "para modificar el arte de CodeCombat. Permite a las unidades lanzar proyectiles, altera la dirección de una animación, cambia los puntos de vida de una unidad o sube tu propio sprite de vectores." + article_editor_prefix: "¿Ves algún error en nuestros documentos? ¿Quieres hacer algunas instrucciones para tus propias creaciones? Revisa el" + article_editor_suffix: "y ayuda a los jugadores de CodeCombat conseguir lo más posible de su tiempo jugando." + find_us: "Encuentranos en etsos sitios" + social_blog: "Lee el blog de CodeCombat en Sett" + social_discource: "Unite a la discusión en nuestro foro" + social_facebook: "Me Gusta CodeCombat en Facebook" + social_twitter: "Sigue a CodeCombat en Twitter" + social_gplus: "Únete a CodeCombat con Google+" + social_hipchat: "Chatea con nosotros en el chat público de CodeCombat en la sala HipChat" + contribute_to_the_project: "Contribuir al proyecto" + + clans: + clan: "Clan" + clans: "Clanes" + new_name: "Nuevo nombre de clan" + new_description: "descripción del clan" + make_private: "Hacer clan privado" + subs_only: "solo suscriptores" + create_clan: "Crear nuevo clan" +# private_preview: "Preview" + public_clans: "Clanes publicos" + my_clans: "Mis Clanes" + clan_name: "Nombre del clan" + name: "Nombre" +# chieftain: "Chieftain" + type: "Tipo" + edit_clan_name: "Editar el nombre del Clan" + edit_clan_description: "Editar descripción del clan" + edit_name: "editar nombre" + edit_description: "editar descripción" + private: "(privado)" +# summary: "Summary" + average_level: "Nivel Promedio" + average_achievements: "Logros Promedio" + delete_clan: "Borrar Clan" + leave_clan: "Abandonar Clan" + join_clan: "Ingresar Clan" + invite_1: "Invitar:" + invite_2: "*Invitar jugadores al clan, mandandoles este link." + members: "Miembros" + progress: "Progreso" + not_started_1: "no iniciado" + started_1: "iniciado" + complete_1: "completo" + exp_levels: "Expand levels" + rem_hero: "Remover Heróe" + status: "Stado" + complete_2: "Completo" + started_2: "Iniciado" + not_started_2: "No inciiado" + view_solution: "Click para ver la solución." + latest_achievement: "último logro" + playtime: "Tiempo de juego" + last_played: "Último jugado" classes: archmage_title: "Archimago" archmage_title_description: "(Desarrollador)" + archmage_summary: "Si eres un programador interesado en juegos educativos, conviértete en un archimago y ayúdanos a construir CodeCombat!" artisan_title: "Artesano" artisan_title_description: "(Constructor de Niveles)" + artisan_summary: "Construye y comparte niveles para que tú y tus amigos jueguen. Conviértete en un Artesano y aprende el arte the enseñar a los demás a programar." adventurer_title: "Aventurero" adventurer_title_description: "(Probador de Niveles)" + adventurer_summary: "Consigue nuestros nuevos niveles| (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Escriba" scribe_title_description: "(Editor de Artículos)" - diplomat_title: "Diplomado" + scribe_summary: "Buen código necesita buena documentación. Escribe, edita y mejora los documentos leídos por millones de jugadores en el mundo." + diplomat_title: "Diplomático" diplomat_title_description: "(Traductor)" + diplomat_summary: "CodeCombat está traducido a más de 45 idiomas por nuestros diplomáticos. Ayúdanos y contribuye con las traducciones." ambassador_title: "Embajador" ambassador_title_description: "(Soporte)" + ambassador_summary: "Ayuda a responder las preguntas de los usuarios del foro. Nuestros Embajadores representan CodeCombat en todo el mundo." -# editor: -# main_title: "CodeCombat Editors" -# article_title: "Article Editor" -# thang_title: "Thang Editor" -# level_title: "Level Editor" -# achievement_title: "Achievement Editor" -# back: "Back" -# revert: "Revert" -# revert_models: "Revert Models" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." -# generate_terrain: "Generate Terrain" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" -# level_some_options: "Some Options?" -# level_tab_thangs: "Thangs" -# level_tab_scripts: "Scripts" -# level_tab_settings: "Settings" -# level_tab_components: "Components" -# level_tab_systems: "Systems" -# level_tab_docs: "Documentation" -# level_tab_thangs_title: "Current Thangs" -# level_tab_thangs_all: "All" -# level_tab_thangs_conditions: "Starting Conditions" -# level_tab_thangs_add: "Add Thangs" -# delete: "Delete" -# duplicate: "Duplicate" -# rotate: "Rotate" -# level_settings_title: "Settings" -# level_component_tab_title: "Current Components" -# level_component_btn_new: "Create New Component" -# level_systems_tab_title: "Current Systems" -# level_systems_btn_new: "Create New System" -# level_systems_btn_add: "Add System" -# level_components_title: "Back to All Thangs" -# level_components_type: "Type" -# level_component_edit_title: "Edit Component" -# level_component_config_schema: "Config Schema" -# level_component_settings: "Settings" -# level_system_edit_title: "Edit System" -# create_system_title: "Create New System" -# new_component_title: "Create New Component" -# new_component_field_system: "System" -# new_article_title: "Create a New Article" -# new_thang_title: "Create a New Thang Type" -# new_level_title: "Create a New Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" -# article_search_title: "Search Articles Here" -# thang_search_title: "Search Thang Types Here" -# level_search_title: "Search Levels Here" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" -# pop_i18n: "Populate I18N" + editor: + main_title: "Editor de CodeCombat" + article_title: "Editor de Artículo" + thang_title: "Editor de Thangs" + level_title: "Editor de Nivel" + achievement_title: "Editor de logros" + poll_title: "Editor de Encuesta" + back: "Atrás" + revert: "Revertir" + revert_models: "Revertir Modelos" + pick_a_terrain: "Elije un Terreno" + dungeon: "Calabozo" + indoor: "Interior" + desert: "Desierto" + grassy: "Herboso" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Pequeño" + large: "Grande" + fork_title: "Fork de Nueva Versión" + fork_creating: "Creando Fork..." + generate_terrain: "Generar terreno" + more: "Más" + wiki: "Wiki" + live_chat: "Chat en vivo" + thang_main: "Principal" + thang_spritesheets: "Spritesheets" + thang_colors: "Colores" + level_some_options: "¿Algunas opciones?" + level_tab_thangs: "Thangs" + level_tab_scripts: "Scripts" + level_tab_settings: "Opciones" + level_tab_components: "Componentes" + level_tab_systems: "Sistemas" + level_tab_docs: "Documentación" + level_tab_thangs_title: "Thangs Actuales" + level_tab_thangs_all: "Todo" + level_tab_thangs_conditions: "Condiciones Iniciales" + level_tab_thangs_add: "Agregar Thangs" +# level_tab_thangs_search: "Search thangs" + add_components: "Agregar Componentes" + component_configs: "Configuraciones del Componente" + config_thang: "Doble clic para configurar un thang" + delete: "Borrar" + duplicate: "Duplicar" + stop_duplicate: "Parar de Duplicar" + rotate: "Rotar" + level_settings_title: "Opciones" + level_component_tab_title: "Componentes Actuales" + level_component_btn_new: "Crear Nuevo Componente" + level_systems_tab_title: "Sistemas Actuales" + level_systems_btn_new: "Crear Nuevo Sistema" + level_systems_btn_add: "Agregar Sistema" + level_components_title: "Regresar a todos los Thangs" + level_components_type: "Tipo" + level_component_edit_title: "Editar Componente" + level_component_config_schema: "Config Schema" + level_component_settings: "Opciones" + level_system_edit_title: "Editar Sistema" + create_system_title: "Crear Nuevo Sistema" + new_component_title: "Crear Nuevo Componente" + new_component_field_system: "Sistema" + new_article_title: "Crear un Nuevo Artículo" + new_thang_title: "Crear un Nuevo tipo de Thang" + new_level_title: "Crear un Nuevo Nivel" + new_article_title_login: "Ingresa para Crear un Nuevo Artículo" + new_thang_title_login: "Ingresa para crear un nuevo tipo de Thang" + new_level_title_login: "Ingresa para Crear un Nuevo Nivel" + new_achievement_title: "Crear un Nuevo Logro" + new_achievement_title_login: "Ingresa para Crear un Nuevo Logro" + new_poll_title: "Crear una nueva encuesta" + new_poll_title_login: "Ingresa para crear una nueva encuesta" + article_search_title: "Buscar Artículos aquí" + thang_search_title: "Buscar tipos de Thang aquí" + level_search_title: "Buscar Niveles aquí" + achievement_search_title: "Buscar logros" + poll_search_title: "Buscar Encuesta" + read_only_warning2: "Nota: no puedes guardar ediciones aquí, porque no estas logueado." + no_achievements: "No hay logros agregados en este nivel por ahora." + achievement_query_misc: "Objetivo clave de misceláneo" + achievement_query_goals: "Objetivo clave de los objetivos de nivel" + level_completion: "Nivel Completado" + pop_i18n: "Poblar I18N" + tasks: "Tareas" + clear_storage: "Borrar tus cambios locales" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Vista previa" edit_article_title: "Editar Artículo" -# contribute: -# page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." -# class_attributes: "Class Attributes" -# archmage_attribute_1_pref: "Knowledge in " -# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." -# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" -# join_desc_1: "Anyone can help out! Just check out our " -# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " -# join_desc_3: ", or find us in our " -# join_desc_4: "and we'll go from there!" -# join_url_email: "Email us" -# join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" -# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." -# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" -# artisan_introduction_suf: ", then this class might be for you." -# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" -# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." -# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" -# artisan_join_desc: "Use the Level Editor in these steps, give or take:" -# artisan_join_step1: "Read the documentation." -# artisan_join_step2: "Create a new level and explore existing levels." -# artisan_join_step3: "Find us in our public HipChat room for help." -# artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" -# artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" -# adventurer_forum_url: "our forum" -# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" -# adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " -# scribe_introduction_url_mozilla: "Mozilla Developer Network" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." -# contact_us_url: "Contact us" -# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" -# scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." -# diplomat_introduction_pref: "So, if there's one thing we learned from the " -# diplomat_launch_url: "launch in October" -# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." -# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." -# diplomat_join_pref_github: "Find your language locale file " -# diplomat_github_url: "on GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" -# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." -# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" -# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" -# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." -# changes_auto_save: "Changes are saved automatically when you toggle checkboxes." -# diligent_scribes: "Our Diligent Scribes:" -# powerful_archmages: "Our Powerful Archmages:" -# creative_artisans: "Our Creative Artisans:" -# brave_adventurers: "Our Brave Adventurers:" -# translating_diplomats: "Our Translating Diplomats:" -# helpful_ambassadors: "Our Helpful Ambassadors:" + polls: + priority: "Prioridad" + + contribute: + page_title: "Contribuyendo" + intro_blurb: "CodeCombat es 100% open source! Cientos de jugadores dedicados nos han ayudado a contruir el juego. Únete y escribe el siguiente capítulo de la misión de CodeCombat de enseñar al mundo a programar!" + alert_account_message_intro: "¡Hola!" + alert_account_message: "Para suscribirte para los correos, necesitas ingresar primero." + archmage_introduction: "Una de las mejores partes de hacer juegos es que sintetizan muchas cosas diferentes. Gráficas, sonido, redes, redes sociales y muchos aspectos comunes de programación, desde manejo de bases de datos y administración de servidores, hasta trabajar en el diseño y construcción de interfaces. Hay mucho para hacer, y si eres un programador con experiencia con el deseo de ingresar en el meollo del asunto de CodeCombat, esta clase puede ser para ti. Nos encantaría contar con tu ayuda para construir el mejor juego de programación." + class_attributes: "Atributos de Clase" + archmage_attribute_1_pref: "Conocimiento en " + archmage_attribute_1_suf: ", o un deseo de aprender. La mayor parte de nuestro código está en este lenguaje. Si eres un fan de Python o Ruby, te sentirás en casa. Es Javascript, pero con un mejor syntax." + archmage_attribute_2: "Alguna experiencia programando e iniciativa personal. Te ayudaremos a orientarte, pero no podemos perder mucho tiempo entrenando." + how_to_join: "Unirse:" + join_desc_1: "¡Cualquiera puede unirse! Sólo checa nuestro " + join_desc_2: "para comenzar, y pon un check abajo para marcarte como un valiente Archimago y conseguir las últimas noticias por email. ¿Quieres chatear sobre qué hacer o cómo involucrarte más? " + join_desc_3: ", o encuéntranos en " + join_desc_4: "y ahí empezaremos!" + join_url_email: "Escríbenos" + join_url_hipchat: "chat público HipChat" + archmage_subscribe_desc: "Obten correos de nuevas oportunidades y anuncios." + artisan_introduction_pref: "¡Debemos construir niveles adicionales! La gente ruega por más contenido, y podemos hacer tanto por nosotros mismos. De momento tu estación de trabajo es nivel 1 ; Nuestro editor de niveles es apenas útil incluso para sus creadores, así que sea cauteloso. Si tuviera visiones de campañas apareciendo para ciclos for" + artisan_introduction_suf: ", entonces esta lase es ideal para ti." + artisan_attribute_1: "Cualquier experiencia creando contenido similar estaría bien, como por ejemplo el editor de niveles de Blizzard. ¡Aunque no es necesaria!" + artisan_attribute_2: "Un anhelo de hacer un montón de pruebas e iteraciones. Para hacer buenos niveles necesitas mostrárselos a otros y mirar como juegan, además de estar preparado para encontrar los fallos a reparar." + artisan_attribute_3: "Por el momento, la resistencia va a la par con el Aventurero. Nuestro editor de niveles está a un nivel de desarrollo temprano y puede ser muy frustrante usarlo. ¡Estás advertido!" + artisan_join_desc: "Sigue las siguientes indicaciones para usar el editor de niveles. Tómalo o déjalo:" + artisan_join_step1: "Lee la documentación." + artisan_join_step2: "Crea un nuevo nivel y explora los niveles existentes." + artisan_join_step3: "Busca nuestra sala pública de HipChat en busca de ayuda." + artisan_join_step4: "Publica tus niveles en el foro para recibir comentarios críticos." + artisan_subscribe_desc: "Recibe correos sobre actualizaciones del editor de niveles y anuncios." + adventurer_introduction: "Hablemos claro sobre tu papel: eres el tanque. Vas a recibir fuertes daños. Necesitamos gente para probar nuestros flamantes niveles y ayudar a mejorarlos. El dolor será enorme; hacer buenos juegos es un proceso largo y nadie lo consigue a la primera. Si puedes resistir y tener una puntuación alta en resistencia, entonces esta clase es para ti." + adventurer_attribute_1: "Estar sediento de conocimientos. Quieres aprender a programar y nosotros queremos enseñarte a hacerlo. Aunque en este caso es más probable que seas tú el que esté haciendo la mayor parte de la enseñanza." + adventurer_attribute_2: "Carismático. Se amable pero claro a la hora de desglosar qué necesita ser mejorado y sugiere de qué formas podría hacerse." + adventurer_join_pref: "Reúnete con (¡o recluta!) un Artesano y trabaja con ellos, o marca la casilla de abajo para recibir un correo cuando haya nuevos niveles para probar. También publicaremos en nuestras redes nuevos niveles para revisar" + adventurer_forum_url: "nuestro foro" + adventurer_join_suf: "así que si prefieres estar informado en esa forma, ¡crea una cuenta allí!" + adventurer_subscribe_desc: "Recibe correos cuando haya nuevos niveles para testar." + scribe_introduction_pref: "CodeCombat no será solo un montón de niveles. También será una fuente de conocimientos, una wiki de conceptos de programación a la que los niveles se engancharan. De esa forma, en lugar de que cada Artesano tenga que describir en detalle qué es un operador de comparación, podrá simplemente enlazar el nivel al Artículo que los describe y que ya ha sido escrito para edificación del jugador. Algo en la línea de lo que la " + scribe_introduction_url_mozilla: "Mozilla Developer Network" + scribe_introduction_suf: " ha construido. Si tu idea de diversión es articular los conceptos de la programación de una forma sencilla, entonces esta clase es para ti." + scribe_attribute_1: "Habilidad a la hora de escribir es casi todo lo que necesitas. No solo dominar la gramática y la ortografía sino también expresar ideas complicadas a los demás de forma sencilla." + contact_us_url: "Escribenos un correo electrónico" + scribe_join_description: "cuéntanos más sobre ti, tu experiencia en el mundo de la programación y sobre qué cosas te gustaría escribir. ¡Y continuaremos a partir de ahí!" + scribe_subscribe_desc: "Recibe correos sobre anuncios de redacción de Artículos." + diplomat_introduction_pref: "Así, si hemos aprendido algo desde el " + diplomat_launch_url: "lanzamiento en octubre" + diplomat_introduction_suf: "hay un interés considerable en CodeCombat en otros paises, ¡especialmente Brasil! Estamos formando un cuerpo de traductores con ganas de traducir un grupo de palabras tras otro para hacer CodeCombat tan accesible para todo el mundo como sea posible. Si quieres recibir avances de próximos contenidos y quieres poner esos niveles a disposición de los que comparten tu idioma tan pronto como sea posible, entonces esta Clase es para ti." + diplomat_attribute_1: "Fluidez con el ingles y el lenguaje al que quieras traducir. Cuando de transmitir ideas complejas se trata, ¡es importante tener grandes conocimientos de ambas!" + diplomat_i18n_page_prefix: "Puedes traducir nuestros niveles yendo a nuestra" + diplomat_i18n_page: "página de traducciones" + diplomat_i18n_page_suffix: ", o en nuestra interfaz y sitio web en GitHub." + diplomat_join_pref_github: "Encuentra el fichero local de tu idioma " + diplomat_github_url: "en GitHub" + diplomat_join_suf_github: ", edítalo online, y solicita que sea revisado. Además, marca la casilla de abajo para mantenerte informado en nuevos progresos en Internacionalización." + diplomat_subscribe_desc: "Recibe correos sobre nuevos niveles y desarrollos para traducir." + ambassador_introduction: "Esta es una comunidad en construcción y tú eres parte de las conexiones. Tenemos chat Olark, correos electrónicos y las redes sociales con una gran cantidad de personas con quienes hablar, ayudar a familiarizarse con el juego y aprender. Si quieres ayudar a la gente a que se involucre, se divierta, y tenga buenas sensaciones sobre CodeCombat y hacia dónde vamos, entonces esta clase es para ti." + ambassador_attribute_1: "Habilidades de comunicación. Ser capaz de identificar los problemas que los jugadores están teniendo y ayudarles a resolverlos. Además, mantener al resto de nosotros informados sobre lo que los jugadores están diciendo, lo que les gusta, lo que no ¡y de lo que quieren más!" + ambassador_join_desc: "cuéntanos más sobre ti, que has hecho y qué estarías interesado en hacer. ¡Y continuaremos a partir de ahí!" + ambassador_join_note_strong: "Nota" + ambassador_join_note_desc: "Una de nuestras principales prioridades es construir un modo multijugador donde los jugadores con mayores dificultades a la hora de resolver un nivel, puedan invocar a los magos más avanzados para que les ayuden. Será una buena manera de que los Embajadores puedan hacer su trabajo. ¡Te mantendremos informado!" + ambassador_subscribe_desc: "Recibe correos sobre actualizaciones de soporte y desarrollo del multijugador." + changes_auto_save: "Los cambios son guardados automáticamente cuando marcas las casillas de verificación." + diligent_scribes: "Nuestros diligentes Escribas:" + powerful_archmages: "Nuestros poderosos Archimagos:" + creative_artisans: "Nuestros creativos Artesanos:" + brave_adventurers: "Nuestros bravos Aventureros:" + translating_diplomats: "Nuestros políglotas Diplomáticos:" + helpful_ambassadors: "Nuestros amables Embajadores:" ladder: please_login: "Por favor inicia sesión antes de jugar una partida de escalera." @@ -707,7 +1011,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip games_simulated: "Juegos simulados" games_played: "Juegos jugados" ratio: "Proporción" - leaderboard: "Posiciones" + leaderboard: "Tabla de Posiciones" battle_as: "Combate como " summary_your: "Tus " summary_matches: "Partidas - " @@ -719,13 +1023,13 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip rank_submitted: "Enviado para Clasificación" rank_failed: "Fallo al Clasificar" rank_being_ranked: "Juego Siendo Clasificado" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" + rank_last_submitted: "Presentado" + help_simulate: "Ayudar simulando juego?" code_being_simulated: "Tu nuevo código está siendo simulado por otros jugadores para clasificación. Esto se refrescará a medida que vengan nuevas partidas." no_ranked_matches_pre: "Sin partidas clasificadas para el " no_ranked_matches_post: " equipo! Juega en contra de algunos competidores y luego vuelve aquí para ver tu juego clasificado." choose_opponent: "Escoge un Oponente" -# select_your_language: "Select your language!" + select_your_language: "Selecciona tu idioma" tutorial_play: "Juega el Tutorial" tutorial_recommended: "Recomendado si nunca has jugado antes" tutorial_skip: "Saltar Tutorial" @@ -733,58 +1037,90 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip tutorial_play_first: "Juega el Tutorial primero." simple_ai: "IA Simple" warmup: "Calentamiento" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + friends_playing: "Amigos Jugando" + log_in_for_friends: "Ingresa para jugar con tus amigos!" + social_connect_blurb: "Conectate y juega contra tus amigos!" + invite_friends_to_battle: "Invita a tus amigos para que se unan a la Batalla!" + fight: "Pelea!" + watch_victory: "Observa tu Victoria" + defeat_the: "Derrota a" + tournament_started: ", iniciado" + tournament_ends: "Final de Torneo" + tournament_ended: "Finalizó el Torneo" + tournament_rules: "Reglas del Torneo" + tournament_blurb: "Escribe código, recolecta oro, arma ejercitos, aplasta adversarios, gana premios, y asciende en tu carrera por $40,000 en el Torneo Codicia! Echa un vistazo a los detalles" + tournament_blurb_criss_cross: "Gana apuestas, construye caminos, burla tus oponentes, agarra gemas, y asciende tu perfil en nuestro torneo Cruzado! Echa un vistazo a los detalles" + tournament_blurb_zero_sum: "Suelta tus habilidades de código en recolección de oro y tácticas de batalla en este partido espejo alpino entre el hechicero rojo y el hechicero azul. El torneo comenzó el Viernes, 27 de Marzo y se extenderá hasta el Lunes, 6 de Abril a las 5PM PDT. Compite por la diversión y la gloria ! Echa un vistazo a los detalles:" + tournament_blurb_blog: "en nuestro blog" + rules: "Reglas" + winners: "Ganadores" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + user: + stats: "Estados" + singleplayer_title: "Nivel un jugador" + multiplayer_title: "Niveles multijugador" + achievements_title: "Logros" + last_played: "Último jugado" + status: "Estado" + status_completed: "Completo" + status_unfinished: "Incompleto" + no_singleplayer: "No hay juegos para un jugador todavía." + no_multiplayer: "No hay juegos multijugador todavía." + no_achievements: "Sin Logros todavía." + favorite_prefix: "Idioma favorito " + favorite_postfix: "." + not_member_of_clans: "No se es miembro de ningún clan todavía." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + achievements: + last_earned: "Último Ganado" + amount_achieved: "Cantidad" + achievement: "Logros" + category_contributor: "Contribuidor" + category_ladder: "Escalera" + category_level: "nivel" + category_miscellaneous: "Misceláneo" + category_levels: "Niveles" + category_undefined: "Sin Categoría" + current_xp_prefix: "" + current_xp_postfix: " en total" + new_xp_prefix: "" + new_xp_postfix: " ganado" + left_xp_prefix: "" + left_xp_infix: " hasta el nivel " + left_xp_postfix: "" -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." + account: + recently_played: "Recientemente jugado" + no_recent_games: "No juegos jugados duramente las últimas dos semanas." + payments: "Pagos" + purchased: "Comprado" + subscription: "Suscripción" + invoices: "Facturas" + service_apple: "Apple" + service_web: "Web" + paid_on: "Pagado en" + service: "Servicio" + price: "Precio" + gems: "Gemas" + active: "Activo" + subscribed: "Suscripto" + unsubscribed: "Insuscripto" + active_until: "Activo Hasta" + cost: "Costo" + next_payment: "Próximo Pago" + card: "Tarjeta" + status_unsubscribed_active: "No estas suscripto y no se te cobrará, pero tu cuenta está activa por ahora." + status_unsubscribed: "Obtén acceso a nuevos niveles, heroés, items y gemas extras con la suscripción a CodeCombat!" + + account_invoices: + amount: "Cantidad en dólares." + declined: "La tarjeta fue rechazada." + invalid_amount: "Por favor ingrese cantidad en dólares." + not_logged_in: "Ingresate o crea una cuenta para acceder a las facturas." + pay: "Pagar Factura" + purchasing: "Comprando..." + retrying: "Error de Servidor, reintentando..." + success: "Listo, fue cobrado. Gracias!" loading_error: could_not_load: "Error cargando del servidor" @@ -800,7 +1136,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip unknown: "Error desconocido." resources: -# sessions: "Sessions" + sessions: "Sesiones" your_sessions: "Tus sesiones" level: "Nivel" social_network_apis: "APIs de Redes Sociales" @@ -809,160 +1145,166 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip facebook_friend_sessions: "Sesiones de Amigos de Facebook" gplus_friends: "Amigos de G+" gplus_friend_sessions: "Sesiones de Amigos de G+" - leaderboard: "Clasificación" + leaderboard: "Tabla de Posiciones" user_schema: "Esquema de Usuario" user_profile: "Perfil de Usuario" + patch: "Parche" patches: "Parches" -# patched_model: "Source Document" + patched_model: "Documento fuente" model: "Modelo" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" -# thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" -# feedback: "Feedback" + system: "Sistema" + systems: "Sistemas" + component: "Componente" + components: "Componentes" + thang: "Thang" + thangs: "Thangs" + level_session: "Tu Sesión" + opponent_session: "Sesión del Oponente" + article: "Artícule" + user_names: "Nombres de usuario" + thang_names: "Nombres de thang" + files: "Archivos" + top_simulators: "Mejores simuladores" + source_document: "Documento fuente" + document: "Documento" + sprite_sheet: "Hoja de Sprite" + employers: "Empleadores" + candidates: "Candidatos" + candidate_sessions: "Sesión de candidato" + user_remark: "Observación del usuario" + user_remarks: "Observaciones del usuario" + versions: "Versiones" + items: "Items" + hero: "Heróe" + heroes: "Héroes" + achievement: "Logros" + clas: "CLAs" + play_counts: "Conteo de juegos" + feedback: "Feedback" + payment_info: "Información de pago" + campaigns: "Campañas" + poll: "Encuesta" + user_polls_record: "Historia de Visitas de Encuestas" -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" -# guide: -# temp: "Temp" + delta: + added: "Agregado" + modified: "Modificado" +# not_modified: "Not Modified" + deleted: "Borrado" + moved_index: "Índice movido" + text_diff: "Diferir Texto" + merge_conflict_with: "UNIR CONFLICTO CON" + no_changes: "Sin cambios" + + guide: + temp: "Temp" multiplayer: multiplayer_title: "Configuración de Multijugador" # We'll be changing this around significantly soon. Until then, it's not important to translate. -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." + multiplayer_toggle: "Activar Multijugador" + multiplayer_toggle_description: "Permitir a otros ingresar a tu juego" multiplayer_link_description: "Da este enlace a cualquiera para que se te una." multiplayer_hint_label: "Consejo:" multiplayer_hint: " Cliquea el enlace para seleccionar todo, luego presiona ⌘-C o Ctrl-C para copiar el enlace." multiplayer_coming_soon: "¡Más características de multijugador por venir!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." + multiplayer_sign_in_leaderboard: "Entra o crea una cuenta y mira tu solución en la tabla de posiciones." -# legal: -# page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." -# opensource_description_prefix: "Check out " -# github_url: "our GitHub" -# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " -# archmage_wiki_url: "our Archmage wiki" -# opensource_description_suffix: "for a list of the software that makes this game possible." -# practices_title: "Respectful Best Practices" -# practices_description: "These are our promises to you, the player, in slightly less legalese." -# privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." -# security_title: "Security" -# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." -# email_title: "Email" -# email_description_prefix: "We will not inundate you with spam. Through" -# email_settings_url: "your email settings" -# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." -# cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." -# copyrights_title: "Copyrights and Licenses" -# contributor_title: "Contributor License Agreement" -# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" -# cla_url: "CLA" -# contributor_description_suffix: "to which you should agree before contributing." -# code_title: "Code - MIT" -# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" -# mit_license_url: "MIT license" -# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." -# art_title: "Art/Music - Creative Commons " -# art_description_prefix: "All common content is available under the" -# cc_license_url: "Creative Commons Attribution 4.0 International License" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" -# art_music: "Music" -# art_sound: "Sound" -# art_artwork: "Artwork" -# art_sprites: "Sprites" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." -# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." -# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" -# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." -# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." -# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." -# rights_title: "Rights Reserved" -# rights_desc: "All rights are reserved for Levels themselves. This includes" -# rights_scripts: "Scripts" -# rights_unit: "Unit configuration" -# rights_description: "Description" -# rights_writings: "Writings" -# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." -# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." -# nutshell_title: "In a Nutshell" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." + legal: + page_title: "Legal" + opensource_intro: "CodeCombat es completamente open source." + opensource_description_prefix: "Echa un vistazo " + github_url: "nuestro GitHub" + opensource_description_center: "y ayudanos si quieres! CodeCombat esta construido por docenas de proyectos open source, y los amamos. Mira " + archmage_wiki_url: "nuestra wiki de Archimago" + opensource_description_suffix: "Para la lista de softwares que hacen al juego posible." + practices_title: "Mejores prácticas respetuosas" + practices_description: "Estas son nuestras promesas hacia ti, el jugador, en términos menos legales." + privacy_title: "Privacidad" + privacy_description: "No vederemos nada sobre tu información personalWe will not sell any of your personal information." + security_title: "Seguridad" + security_description: "Queremos mantener tu información personal privada. Como un proyecto open source, cualquiera puede revisar y mejorar nuestros sistemas de seguridad." + email_title: "Mail" + email_description_prefix: "No te vamos a inundar de Spam. Mediante" + email_settings_url: "tus opciones de mail" + email_description_suffix: "o mediante links en los mails que mandamos, tu puedas cambiar tus preferencias y fácilmente desuscribirte en cualquier momento." + cost_title: "Costo" + cost_description: "CodeCombat es gratuito para todos sus niveles principales, con una suscripción de $9.99 USD/mes con acceso a niveles adicionales y un bonus de 3500 gemas cada mes. Puedes cancelar con un click y ofrecemos una garantía del 100%." + copyrights_title: "Derechos y Licencias" + contributor_title: "Contributor License Agreement" + contributor_description_prefix: "Todas las Contribuciones, tanto en el website como en nuestro Repositorio GitHub, estan sujetos a nuestra aprobación." + cla_url: "CLA" + contributor_description_suffix: "A lo que debes de estar de acuerdo antes de contribuir." + code_title: "Código - MIT" + code_description_prefix: "Todo el código pertenece a CodeCombat u hospedado en codecombat.com, sea en el repositorio GitHub o en la base de datos en codecombat.com, está bajo licencia" + mit_license_url: "Licencia MIT" + code_description_suffix: "Esto incluye todo el código en Systems and Components that are made available by CodeCombat for the purpose of creating levels." + art_title: "Arte/Música - Comunas Creativas " + art_description_prefix: "Todo el Contenido Comunal está disponible bajo la" + cc_license_url: "Licencia Internacional de Atribución Comunas Creativas (CC) 4.0" + art_description_suffix: "Contenido Comunal es cualquiera hecho por CodeCombat y disponile con el propósito de crear niveles. Esto incluye:" + art_music: "Música" + art_sound: "Sonido" + art_artwork: "Trabajo Artístico" + art_sprites: "Sprites" + art_other: "Cualquier otro trabajo creativo (no necesariamente código) que están disponibles cuando se crean niveles." + art_access: "Actualmente no hay un sistema fácil y universal para obtener estos activos. En general, obtenlos de los URLs tal y como son usados por el sitio, contáctanos si necesitas ayuda, o ayúdanos extendiendo el sitio para hacer estos activos accesibles más fácilmente." + art_paragraph_1: "Para atribución, por favor nombre y enlace a codecombat.com cerca de donde esta fuente sea usada o donde sea apropiado en el medio usado. Por ejemplo:" + use_list_1: "Si es usado en una película o en otro juego, incluya codecombat.com en los créditos." + use_list_2: "Si es usado en un website, incluya un link cerca donde es usado, por ejemplo debajo de una imagen, o en las atribuciones generales de la página, donde pueda mencionar también otros trabajos bajo Creative Commons o de código abierto que sean usados en el sitio web. Cualquier otro website que haga clara referencia a CodeCombat, tal como un blog post mencionando a CodeCombat, no necesita una atribución separada." + art_paragraph_2: "Si el contenido usado no fue creado por CodeCombat, sino por un usuario de codecombat.com, atribuya al usuario y siga las directivas de atribución provistas en la descripción del recurso, en caso las haya." + rights_title: "Derechos Reservados" + rights_desc: "Todos los derechos estan reservados para los niveles mismos. Esto incluye." + rights_scripts: "Scripts" + rights_unit: "Configuración de la unidad" + rights_description: "Descripción" + rights_writings: "Escritos" + rights_media: "Media (sonidos, música) y cualquier otro contenido creativo hecho específicamente para un nivel y que no haya sido hecho disponible al público cuando se crearon los niveles." + rights_clarification: "Aclarando, todo contenido puesto a disposición en el Editor de Niveles con el propósito de hacer más niveles se encuentra bajo licencia CC, mientras todo contenido creado con el Editor de Niveles o cargado durante la creación de Niveles no lo está." + nutshell_title: "En una palabra" + nutshell_description: "Cualquier recurso que te proveamos en el Editor de Niveles es gratis de usar como te plazca para la creación de Niveles. Sin embargo, nos reservamos el derecho de restringir la distribución de los niveles por sí mismos (aquellos creados en codecombat.com) para así poder cobrar por ellos en el futuro, si es que eso es lo que termina pasando." + canonical: "La versión en inglés de este documento es la versión canónica y definitiva. Si hay alguna discrepancia entre las traducciones, la versión en inglés toma precedencia." -# ladder_prizes: -# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - - wizard_settings: - title: "Configuración del mago" - customize_avatar: "Personaliza tu avatar" - active: "Activo" - color: "Color" - group: "Grupo" - clothes: "Ropa" - trim: "Recortar" - cloud: "Nube" - team: "Equipo" - spell: "Hechizo" - boots: "Botas" - hue: "Matiz" - saturation: "Saturación" - lightness: "Brillo" + ladder_prizes: + title: "Premios de Torneos" # This section was for an old tournament and doesn't need new translations now. + blurb_1: "Estos premios seran dados de acuerdo a " + blurb_2: "las reglas del torneo" + blurb_3: "a los mejores jugadores humanos y ogros." + blurb_4: "Dos equipos significan el doble de premios!" + blurb_5: "(Habrán dos ganadores en el primer puesto, dos en el segundo puesto, etc.)" + rank: "Ranking" + prizes: "Premios" + total_value: "Valor Total" + in_cash: "en dinero" + custom_wizard: "CodeCombat Mago Personalizado" + custom_avatar: " CodeCombat Avatar Personalizado" + heap: "Por seis meses acceso \"Startup\"." + credits: "creditos" + one_month_coupon: "Cupón: elegí entre Rails o HTML." + one_month_discount: "descuento del 30%: elegí entre Rails o HTML" + license: "licencia" + oreilly: "ebook de su elección" account_profile: settings: "Configuración" # We are not actively recruiting right now, so there's no need to add new translations for this section. @@ -1070,8 +1412,8 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip player_code: "Código de Jugador" employers: -# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." -# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." + deprecation_warning_title: "Lo sentimos, CodeCombat no esta reclutando en estos momentos." + deprecation_warning: "Estamos enfocados en los niveles de principiante en lugar de buscar desarrolladores expertos por el momento." hire_developers_not_credentials: "Contrata desarrolladores, no credenciales." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. get_started: "Comenzar" already_screened: "Ya hemos realizado un monitoreo técnico de todos los candidatos" @@ -1102,7 +1444,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip what: "Que es CodeCombat?" what_blurb: "CodeCombat es un juego multijugador de programación para navegadores. Los jugadores escriben un código para medirse en batalla contra otros desarrolladores. Nuestros jugadores cuentan con experiencia en los principales lenguajes técnicos." cost: "¿Cuánto cobramos?" - cost_blurb: "Cobramos un 15% del primer salario anual y ofrecemos una garantía de devolución del 100% del dinero por 90 días. No cobramos por candidatos que actualmente se encuentren siendo entrevistados de forma activa por tu compañia." + cost_blurb: "Cobramos un 15% del primer salario anual y ofrecemos una garantía de devolución del 100% del dinero por 90 días. No cobramos por candidatos que actualmente se encuentren siendo entrevistados de forma activa por tu compañía." candidate_name: "Nombre" candidate_location: "Ubicación" candidate_looking_for: "Buscando" @@ -1116,21 +1458,21 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip inactive_developers: "Desarrolladores Inactivos" admin: -# av_espionage: "Espionage" # Really not important to translate /admin controls. -# av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" -# av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" + av_espionage: "Espionaje" # Really not important to translate /admin controls. + av_espionage_placeholder: "Mail o usuario" + av_usersearch: "Buscar Usuario" + av_usersearch_placeholder: "Mail, usuario, nombre, lo que sea" + av_usersearch_search: "Buscar" av_title: "Vistas de Admin" av_entities_sub_title: "Entidades" av_entities_users_url: "Usuarios" av_entities_active_instances_url: "Instancias Activas" -# av_entities_employer_list_url: "Employer List" -# av_entities_candidates_list_url: "Candidate List" -# av_entities_user_code_problems_list_url: "User Code Problems List" + av_entities_employer_list_url: "Lista de Empleadores" + av_entities_candidates_list_url: "Lista de Candidatos" + av_entities_user_code_problems_list_url: "Lista de Usuarios con problemas de código" av_other_sub_title: "Otro" av_other_debug_base_url: "Base (para depurar base.jade)" u_title: "Lista de Usuarios" -# ucp_title: "User Code Problems" + ucp_title: "Usuario con problemas de código" lg_title: "Últimos Juegos" clas: "CLAs" diff --git a/app/locale/es-ES.coffee b/app/locale/es-ES.coffee index 94cb3e038..45845cead 100644 --- a/app/locale/es-ES.coffee +++ b/app/locale/es-ES.coffee @@ -3,14 +3,15 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis slogan: "Aprende a programar jugando" no_ie: "CodeCombat no funciona en Internet Explorer 8 o anteriores. ¡Lo sentimos!" # Warning that only shows up in IE8 and older no_mobile: "¡CodeCombat no fue diseñado para dispositivos móviles y puede que no funcione!" # Warning that shows up on mobile devices - play: "Jugar" # The big play button that just starts playing a level + play: "Jugar" # The big play button that opens up the campaign view. old_browser: "Ay, su navegador es demasiado viejo para ejecutar CodeCombat. ¡Lo sentimos!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Lo puede intentar de todos modos, pero probablemente no va a funcionar." + ipad_browser: "Malas noticias: CodeCombat no corre en el navegador de iPad. Buenas noticias: nuestra aplicación para iPad está esperando la aprobación de Apple." campaign: "Campaña" for_beginners: "Para principiantes" multiplayer: "Multijugador" # Not currently shown on home page for_developers: "Para programadores" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "O descargalo para iPad" nav: play: "Jugar" # The top nav bar entry where players choose which levels to play @@ -52,66 +53,64 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis players: "jugadores" # Hover over a level on /play hours_played: "horas jugadas" # Hover over a level on /play items: "Objetos" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details + unlock: "Desbloquear" # For purchasing items and heroes + confirm: "Confirmar" + owned: "Lo Posees" # For items you own + locked: "Bloqueado" + purchasable: "Comprable" # For a hero you unlocked but haven't purchased + available: "Disponible" + skills_granted: "Habilidades concedidas" # Property documentation details heroes: "Heroes" # Tooltip on hero shop button from /play achievements: "Logros" # Tooltip on achievement list button from /play account: "Cuenta" # Tooltip on account button from /play settings: "Ajustes" # Tooltip on settings button from /play + poll: "Encuesta" # Tooltip on poll button from /play next: "Siguiente Heroe" # Go from choose hero to choose inventory before playing a level change_hero: "Seleccionar Heroe" # Go back from choose inventory to choose hero choose_inventory: "Equipar Objetos" -# buy_gems: "Buy Gems" - older_campaigns: "Campañas Anteriores" + buy_gems: "Comprar Joyas" + subscription_required: "Suscripción requerida" anonymous: "Jugador Anonimo" level_difficulty: "Dificultad: " campaign_beginner: "Campaña de Principiante" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Elige tu nivel" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Puedes elegir cualquier pantalla o charlar en " - adventurer_forum: "el foro del aventurero " - adventurer_suffix: "sobre ello." - campaign_old_beginner: "Antigua Campaña de Principiante" - campaign_old_beginner_description: "... en la que aprenderás la magia de la programación." - campaign_dev: "Niveles aleatorios más dificiles" - campaign_dev_description: "... en los que aprenderás sobre la interfaz mientras haces algo más difícil." + awaiting_levels_adventurer_prefix: "Liberamos cinco niveles cada semana." # {change} + awaiting_levels_adventurer: "Regístrate como Aventurero" + awaiting_levels_adventurer_suffix: "para ser el primero en jugar nuevos niveles." + adjust_volume: "Ajustar volúmen" campaign_multiplayer: "Arenas Multijugador" campaign_multiplayer_description: "... en las que tu código se enfrentará al de otros jugadores." - campaign_player_created: "Creaciones de los Jugadores" - campaign_player_created_description: "... en las que luchas contra la creatividad de tus compañeros <a href=\"/contribute#artisa\">Magos Artesanos</a>." - campaign_classic_algorithms: "Algoritmos Clasicos" - campaign_classic_algorithms_description: "... donde aprendes los algoritmos mas populares de la informatica." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "¡Estás teniendo un gran progreso! Cuéntale a alguien que tanto habeis aprendido con CodeCombat." # {change} + email_invalid: "La dirección de correo electrónico no es válida." + form_blurb: "¡Introduzca su correo electrónico y nosotros les mostraremos!" + form_label: "Correo Electrónico" + placeholder: "correo electrónico" + title: "Excelente Trabajo Aprendiz" login: sign_up: "Crear una cuenta" log_in: "Entrar" logging_in: "Entrando..." log_out: "Salir" - recover: "recuperar cuenta" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "¿Olvidaste tu contraseña?" + authenticate_gplus: "Autenticar G+" + load_profile: "Cargar perfil G+" + finishing: "Finalizando" + sign_in_with_facebook: "Accede usando Facebook" + sign_in_with_gplus: "Accede usando G+" + signup_switch: "¿Quieres crear una cuenta?" signup: - create_account_title: "Crea una cuenta para guardar tu progreso" - description: "¡Es gratis!. ¡Solo necesitamos un par de cosas y listo para comenzar!" email_announcements: "Recibir noticias por correo electrónico" - coppa: "Soy mayor de 13 o de fuera de los Estados Unidos" - coppa_why: "(¿Por qué?)" creating: "Creando cuenta..." sign_up: "Registrarse" log_in: "Iniciar sesión con contraseña" social_signup: "O, puedes acceder a través de tu cuenta de Facebook o G+:" required: "Tienes que estar reginstrado antes de poder seguir por aquí." + login_switch: "¿Ya tienes una cuenta?" recover: recover_account_title: "Recuperar Cuenta" @@ -119,14 +118,16 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis recovery_sent: "Email de recuperación de contraseña enviado." items: -# primary: "Primary" -# secondary: "Secondary" + primary: "Primario" + secondary: "Secundario" armor: "Armadura" accessories: "Accesorios" misc: "Miscelanea" -# books: "Books" + books: "Libros" common: + back: "Volver" # When used as an action verb, like "Navigate backward" + continue: "Continuar" # When used as an action verb, like "Continue forward" loading: "Cargando..." saving: "Guardando..." sending: "Enviando..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis fork: "Bifurcar" play: "Jugar" # When used as an action verb, like "Play next level" retry: "Reintentar" + actions: "Acciones" + info: "Información" + help: "Ayuda" watch: "Mirar" unwatch: "Pasar" - submit_patch: "Mandar Parche" + submit_patch: "Enviar Parche" + submit_changes: "Enviar Cambios" +# save_changes: "Save Changes" general: and: "y" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis date: "Fecha" body: "Cuerpo" version: "Versión" + pending: "Pendiente" + accepted: "Aceptado" + rejected: "Rechazado" + withdrawn: "Retirado" + submitter: "Submitter" + submitted: "Enviado" commit_msg: "Mensaje de Asignación o Commit" + review: "Revisión" version_history: "Historial de versión" version_history_for: "Historial de las versiones de: " + select_changes: "Selecciona dos cambios más abajo para ver la diferencia." + undo_prefix: "Deshacer" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Rehacer" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Reproducir una vista previa del nivel actual" result: "Resultado" results: "Resultados" description: "Descripción" @@ -173,7 +192,10 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis medium: "Media" hard: "Difícil" player: "Jugador" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Nivel" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Guerrero" + ranger: "Unidad de combate a distancia" + wizard: "Mago" units: second: "segundo" @@ -194,26 +216,27 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis play_level: done: "Hecho" home: "Inicio" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" + level: "Nivel" # Like "Level: Dungeons of Kithgard" skip: "Saltar" game_menu: "Menu del Juego" guide: "Guía" restart: "Reiniciar" goals: "Objetivos" goal: "Objetivo Principal" -# running: "Running..." + running: "Corriendo..." success: "Exito!" incomplete: "Incompleto" timed_out: "Te has quedado sin tiempo" failing: "Fallando" action_timeline: "Cronología de Acción" click_to_select: "Click en una unidad para seleccionarla" -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "Multijugador" + control_bar_join_game: "Unirse a un juego" + reload: "Recargar" reload_title: "¿Recargar todo el código?" reload_really: "¿Estas seguro que quieres reiniciar el nivel?" reload_confirm: "Recargarlo todo" + victory: "Victoria" victory_title_prefix: "¡" victory_title_suffix: " Completado!" victory_sign_up: "Regístrate para recibir actualizaciones." @@ -221,17 +244,16 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis victory_rate_the_level: "Puntúa este nivel: " # Only in old-style levels. victory_return_to_ladder: "Volver a Clasificación" victory_play_continue: "Continuar" - victory_play_skip: "Saltar animacion" - victory_play_next_level: "Jugar el siguiente nivel" - victory_play_more_practice: "Mas Practica" - victory_play_too_easy: "Demasiado facil" - victory_play_just_right: "Justo" - victory_play_too_hard: "Demasiado dificil" victory_saving_progress: "Salvando Progreso" victory_go_home: "Ir a Inicio" # Only in old-style levels. victory_review: "¡Cuéntanos más!" # Only in old-style levels. victory_hour_of_code_done: "¿Ya terminaste?" victory_hour_of_code_done_yes: "Si, ¡He terminado con mi hora de código!" + victory_experience_gained: "XP Conseguida" + victory_gems_gained: "Gemas Conseguidas" + victory_new_item: "Nuevo artículo" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." + victory_become_a_viking: "Convertirse en un vikingo" guide_title: "Guía" tome_minion_spells: "Los hechizos de tus súbditos" # Only in old-style levels. tome_read_only_spells: "Hechizos de solo lectura" # Only in old-style levels. @@ -242,28 +264,36 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis tome_submit_button: "Mandar" tome_reload_method: "Recargcar código original para este método" # Title text for individual method reload button. tome_select_method: "Seleccionar método" - tome_see_all_methods: "Métodos que pueden ser editados" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "Métodos que pueden ser editados" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Selecciona a alguien para " tome_available_spells: "Hechizos disponibles" tome_your_skills: "Tus Habilidades" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" + tome_help: "Ayuda" + tome_current_method: "Método actual" + hud_continue_short: "Continuar" code_saved: "Codigo Salvado" skip_tutorial: "Saltar (esc)" keyboard_shortcuts: "Atajos de teclado" loading_ready: "¡Listo!" loading_start: "Iniciar Nivel" -# problem_alert_title: "Fix Your Code" + problem_alert_title: "Arregla tu código" + problem_alert_help: "Ayuda" time_current: "Ahora:" time_total: "Máx:" time_goto: "Ir a:" + non_user_code_problem_title: "No puede cargar un nivel" + infinite_loop_title: "Bucle infinito detectado" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Inténtalo de nuevo" infinite_loop_reset_level: "Reiniciar nivel" infinite_loop_comment_out: "Comenta mi código" tip_toggle_play: "Alterna entre jugar/pausa con Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ y Ctrl+] rebobina y avanza hacia adelante." + tip_scrub_shortcut: "Ctrl+[ y Ctrl+] rebobina y avanza hacia adelante." # {change} tip_guide_exists: "Haz clic en la guía arriba de la página para más información útil." tip_open_source: "¡CodeCombat es 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat lanzó su beta en Octubre de 2013." tip_think_solution: "Piensa en la solución, no en el problema." tip_theory_practice: "En teoría, no hay diferencia entre la teoría y la práctica. Pero en la práctica, la hay. - Yogi Berra" @@ -286,88 +316,210 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis tip_talk_is_cheap: "Hablar es fácil. Enséñame el código. - Linus Torvalds" tip_first_language: "La cosa más desastrosa que puedes aprender es tu primer lenguaje de programación. - Alan Kay" tip_hardware_problem: "P: Cuantos programadores hacen falta para cambiar una bombilla? R: Ninguno, es un problema de hardware." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." + tip_hofstadters_law: "Ley de Hofstadter: Siempre lleva más tiempo de lo que esperas, incluso cuando tienes en cuenta la Ley de Hofstadter." tip_premature_optimization: "La optimizacion prematura es la raiz de todo mal. - Donald Knuth" tip_brute_force: "Cuando haya dudas, usa la fuerza bruta. - Ken Thompson" - customize_wizard: "Personalizar Mago" + tip_extrapolation: "Existen solo dos clases de personas: aquellos que pueden extrapolar desde información incompleta..." + tip_superpower: "Programar es lo más parecido que tenemos a un superpoder." + tip_control_destiny: "En el verdadero open source, tienes el derecho de controlar tu propio destino. - Linus Torvalds" + tip_no_code: "Ningún código es más rápido que ningún código" + tip_code_never_lies: "El código nunca os miente, los comentarios algunas veces. — Ron Jeffries" + tip_reusable_software: "Antes de que el software pueda ser reutilizable, primero debe ser utilizable." + tip_optimization_operator: "Cada lenguaje tiene un operator para optimización. En la mayoría de los lenguajes dicho operador es ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." + tip_javascript_java: "Java es a JavaScript lo que un automóvil es a un móvil. - Chris Heilmann" + tip_move_forward: "Lo que sea que hagas, sigue hacia adelante. - Martin Luther King Jr." + tip_google: "¿Teneis un problema que no podeis resolver? ¡Googleadlo!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" + tip_recurse: "Iterar es humano, recursar es divino. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" + tip_strong_opponents: "Incluso el más fuerte de los opositores oculta debilidad. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventario" save_load_tab: "Salvar/Cargar" options_tab: "Opciones" guide_tab: "Guia" + guide_video_tutorial: "Vídeo Tutorial" + guide_tips: "Consejos" multiplayer_tab: "Multijugador" auth_tab: "Crear cuenta" - inventory_caption: "Equipa a tu heroe" - choose_hero_caption: "Elige la lengua del heroe" + inventory_caption: "Equipa a tu héroe" + choose_hero_caption: "Elige la lengua del héroe" save_load_caption: "... y ver la historia" - options_caption: "Ajustes de configuracion" + options_caption: "Ajustes de configuración" guide_caption: "Documentos y pistas" multiplayer_caption: "Juega con amigos!" auth_caption: "Salvar tu progreso." + leaderboard: + leaderboard: "Jefe de la liga" + view_other_solutions: "Ver Otras Soluciones" # {change} + scores: "Puntuaciones" +# top_players: "Top Players by" + day: "Hoy" + week: "Esta semana" +# all: "All-Time" + time: "Tiempo" + damage_taken: "Daño recibido" + damage_dealt: "Daño causado" + difficulty: "Difficultad" + gold_collected: "Oro colectado" + inventory: choose_inventory: "Equipar Objetos" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + equipped_item: "Equipado" + required_purchase_title: "Requerido" + available_item: "Disponible" + restricted_title: "Restringido" + should_equip: "(doble-click para equipar)" + equipped: "(equipado)" + locked: "(bloqueado)" + restricted: "(restringido en este nivel)" + equip: "Equipar" + unequip: "Desequipar" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + buy_gems: + few_gems: "Unas pocas joyas" + pile_gems: "Pila de joyas" + chest_gems: "Cofre de joyas" + purchasing: "Comprando..." + declined: "Tu tarjeta fue rechazada" + retrying: "Erroe en servidor, reintentando." + prompt_title: "Gemas no suficientes" + prompt_body: "¿Quieres obtener más?" + prompt_button: "Ingresa a la tienda" + recovered: "Las gemas compradas con anterioridad han sido recuperadas. Por favor, refresca la página." +# price: "x3500 / mo" + + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" + feature5: "Vídeo tutoriales" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" + free: "Gratis" + month: "mes" + subscribe_title: "Suscríbete" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" + thank_you_months_suffix: "meses." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" + parents: "Para Padres" + parents_title: "Tus hijos aprenderan a programar." # {change} + parents_blurb1: "Con CodeCombat, tus hijos aprendes a desarrollar código real. Al inicio aprenden comandos simples, y avanzan a temas más avanzados." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "Por $9.99 USD/mes, tienen nuevos desafios cada semana y un correo personal con soporte de nuestros programadores profesionales." # {change} + parents_blurb3: "Sin riesgo: 100% garantía de devoluación de dinero, desuscripción con un simple click." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "Suscripción mensual" + subscription_required_to_play: "Necesitas una suscripción para jugar este nivel." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: choose_hero: "Selecciona tu Heroe" programming_language: "Lenguaje de Programación" programming_language_description: "¿Qué lenguaje de programación deseas usar?" # default: "Default" -# experimental: "Experimental" + experimental: "Experimental" python_blurb: "Simple pero poderoso." javascript_blurb: "El lenguaje de la web." - coffeescript_blurb: "Sintaxsis de JavaScript mejorada." + coffeescript_blurb: "Sintaxis de JavaScript mejorada." clojure_blurb: "Un Lisp moderno." lua_blurb: "Lenguaje Script para Juegos." io_blurb: "Simple pero oscuro." status: "Estado" + hero_type: "Tipo" weapons: "Armas" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" + weapons_warrior: "Espadas - Corto Alcance, Sin Magia" + weapons_ranger: "Ballestas, Pistolas - Largo Alcance, Sin Magia" + weapons_wizard: "Varitas, Bastones - Largo Alcance, Magia" attack: "Daño" # Can also translate as "Attack" health: "Salud" speed: "Velocidad" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" + regeneration: "Regeneración" + range: "Alcance" # As in "attack or visual range" + blocks: "Bloqueo" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" skills: "Habilidades" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." + speed_1: "Se mueve a" + speed_2: "metros por segundo." + available_for_purchase: "Disponible para Comprar" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Nivel para desbloquear:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Solo ciertos heroes pueden jugar este nivel." -# skill_docs: + skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" + read_only: "Solo lectura" + action_name: "nombres" # action_cooldown: "Takes" # action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + action_damage: "Daño" + action_range: "Alcance" + action_radius: "Radio" + action_duration: "Duración" + example: "Ejemplo" + ex: "ej" # Abbreviation of "example" + current_value: "Valor actual" + default_value: "Valor por defecto" + parameters: "Parámetros" + returns: "Devolver" + granted_by: "Otorgado por" save_load: granularity_saved_games: "Salvado" @@ -378,8 +530,6 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis volume_label: "Volumen" music_label: "Musica" music_description: "Musica de fondo on/off." - autorun_label: "Autorun" - autorun_description: "Control automatico de codigo en ejecucion." editor_config: "Conf. editor" editor_config_title: "Configuración del editor" editor_config_level_language_label: "Lenguaje para este nivel" @@ -389,7 +539,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis editor_config_keybindings_label: "Atajos de teclado" editor_config_keybindings_default: "Actual (Ace)" editor_config_keybindings_description: "Permite el uso de atajos de teclado de algunos editores conocidos." -# editor_config_livecompletion_label: "Live Autocompletion" + editor_config_livecompletion_label: "Autocompletado en vivo" editor_config_livecompletion_description: "Muestra sugerencias de autocompletado mientras se escribe." editor_config_invisibles_label: "Mostrar elementos invisibles" editor_config_invisibles_description: "Se pueden ver elementos invisibles como espacios o tabulaciones." @@ -412,34 +562,128 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis press_paragraph_1_link: "paquete de prensa" press_paragraph_1_suffix: ". Todos los logos y las imagenes pueden ser usados sin necesidad de contactarnos directamente." team: "Equipo" - george_title: "CEO" + george_title: "CEO" # {change} george_blurb: "Hombre de Negocios" - scott_title: "Programador" + scott_title: "Programador" # {change} scott_blurb: "Razonable" - nick_title: "Programador" + nick_title: "Programador" # {change} nick_blurb: "Guru Motivacional" michael_title: "Programador" michael_blurb: "Administrador de Sistemas" - matt_title: "Programador" + matt_title: "Programador" # {change} matt_blurb: "Ciclista" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" + josh_title: "Diseñador de Videojuegos" +# josh_blurb: "Floor Is Lava" + jose_title: "Música" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + + teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" + how_much_2: "suscripción mensual" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Guardar nueva versión" new_major_version: "Nueva versión principal" +# submitting_patch: "Submitting Patch..." cla_prefix: "Para guardar los cambios, primero debes aceptar nuestro" cla_url: "CLA" cla_suffix: "." cla_agree: "De acuerdo" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contacta con CodeCombat" welcome: "¡Nos gusta saber de ti! Usa este formulario para enviarnos un correo. " - contribute_prefix: "Si estás interesado en colaborar, ¡échale un vistazo a nuestra " - contribute_page: "página de contribuciones" - contribute_suffix: "!" forum_prefix: "Para asuntos públicos, por favor usa " forum_page: "nuestro foro" forum_suffix: " en su lugar." + faq_prefix: "Tambien existe un" + faq: "FAQ" + subscribe_prefix: "Si necesitas ayuda para resolver el nivel, por favor" + subscribe: "compra una subscripción de CodeCombat" + subscribe_suffix: "y estaremos encantados de ayudarte con tu código." + subscriber_support: "Ya que eres un subscriptor de CodeCombat, tu email tendrá nuestro soporte prioritario." + screenshot_included: "Pantallazo incluido." + where_reply: "¿Dónde debemos responder?" send: "Envía tu comentario" contact_candidate: "Contactar Candidato" # Deprecated recruitment_reminder: "Usa este formulario para contactar con los candidatos que quieras entrevistar. Recuerda que CodeCombat cobrará el 18% del salario durante el primer año. La cuota es por la contratación del empleado y es reembolsable durante 90 días si el empleado no permanece contratado. A tiempo parcial, a distancia y los empleados de contrato son gratis, como lo son los becarios." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis autosave: "Los cambios se guardan automáticamente" me_tab: "Yo" picture_tab: "Foto" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" upload_picture: "Sube una imagen" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Contraseña" emails_tab: "Correos electrónicos" admin: "Admin" new_password: "Nueva contraseña" new_password_verify: "Verificar" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "Suscripciones de correo electrónico" email_subscriptions_none: "Sin suscripciones de correo electrónico." email_announcements: "Noticias" @@ -481,19 +732,18 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis job_profile_explanation: "¡Hola! Rellena esto y estaremos en contacto para hablar sobre encontrarte un trabajo como desarrollador de software." sample_profile: "Mira un perfil de ejemplo" view_profile: "Mira tu perfil" - wizard_tab: "Mago" - wizard_color: "Color de la ropa del Mago" keyboard_shortcuts: keyboard_shortcuts: "Atajos de teclado" space: "Barra espaciadora (Espacio)" enter: "Enter" +# press_enter: "press enter" escape: "Escape" shift: "Shift" -# run_code: "Run current code." + run_code: "Ejecutar código actual." run_real_time: "correr en tiempo real." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." + continue_script: "Continuar después del script actual." + skip_scripts: "Saltar todos los scripts posibles." # toggle_playback: "Toggle play/pause." # scrub_playback: "Scrub back and forward through time." # single_scrub_playback: "Scrub back and forward through time by a single frame." @@ -503,17 +753,16 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis # toggle_pathfinding: "Toggle pathfinding overlay." beautify: "Embellece tu código estandarizando el formato." maximize_editor: "Maximizar/minimizar editor de codigo." - move_wizard: "Mover a tu hechicero por el nivel." community: main_title: "Comunidad de CodeCombat" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." + introduction: "Descubre las formas en las que te puedes involucrar abajo y decide cual te suena más divertida. ¡Esperamos trabajar contigo!" + level_editor_prefix: "Usa CodeCombat" + level_editor_suffix: "para crear y editar niveles. Usuarios han creado niveles para sus clases, amigos, hackathons, estudiantes, y hermanos. Si crear un nivel te suena intimidante, ¡puedes empezar usando uno de los nuestros!" + thang_editor_prefix: "Llamamos a la unidades dentro del juego 'thangs'. Usa el" + thang_editor_suffix: "para modificar los artes fuente de CodeCombat. Permite a las unidades lanzar proyectiles, cambiar la dirección de la animación, cambiar los puntos de una unidad, o subir tus propios sprites vectoriales." + article_editor_prefix: "¿Viste un error en alguno de nuestros documentos? ¿Quieres hacer algunas instrucciones para tus propias creaciones? Revisa el" + article_editor_suffix: "y ayuda a juegadores de CodeCombat a obtener lo mejor de su tiempo en el juego." find_us: "Encuentranos en estos sitios" social_blog: "Lee el blog de CodeCombat en Sett" social_discource: "Unete a la discusion en nuestro foro" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis social_hipchat: "Habla con nosotros en el chat publico de CodeCombat HipChat room" contribute_to_the_project: "Contribuye al proyecto" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "Archimago" archmage_title_description: "(Programador)" + archmage_summary: "¡Si eres un desarrollador interesado en crear juegos educacionales, conviértete en un Archimago y ayuda a la construcción de CodeCombat!" artisan_title: "Artesano" artisan_title_description: "(Diseñador de Niveles)" + artisan_summary: "Construye y comparte niveles para que puedas jugar tu y tus amigos. Conviértete en un Artesano para aprender el arte de enseñar a otros a programar." adventurer_title: "Aventurero" adventurer_title_description: "(Tester de Niveles)" + adventurer_summary: "Consigue una semana antes y gratis, los nuevos niveles (incluso aquellos que requieren subscripción) y ayúdanos a solucionar bugs (fallos) antes de que se publiquen los niveles." scribe_title: "Escriba" scribe_title_description: "(Editor de Artículos)" + scribe_summary: "El buen código requiere de buena documentación. Escribe, edita, y mejora la documentación leída por millones de jugadores a lo largo del globo." diplomat_title: "Diplomático" diplomat_title_description: "(Traductor)" + diplomat_summary: "CodeCombat está traducido a más de 45 idiomas por nuestros Diplomáticos. Ayúdanos realizando traducciones." ambassador_title: "Embajador" ambassador_title_description: "(Soporte)" + ambassador_summary: "Amansa a los usuarios de nuestro foro y guía a aquellos que tengan preguntas. Nuestros Embajadores representan a CodeCombat frente al mundo." editor: main_title: "Editores de CodeCombat" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis thang_title: "Editor de Objetos" level_title: "Editor de Niveles" achievement_title: "Editor de Logros" +# poll_title: "Poll Editor" back: "Volver" revert: "Revertir" revert_models: "Revertir Modelos" pick_a_terrain: "Escoge un Terreno" - small: "Pequeño" +# dungeon: "Dungeon" +# indoor: "Indoor" + desert: "Desierto" grassy: "Cubierto de hierba" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Pequeño" +# large: "Large" fork_title: "Bifurcar nueva versión" fork_creating: "Creando bifurcación..." generate_terrain: "Generar Terreno" more: "Más" wiki: "Wiki" live_chat: "Chat en directo" + thang_main: "Principal" +# thang_spritesheets: "Spritesheets" + thang_colors: "Colores" level_some_options: "¿Algunas opciones?" level_tab_thangs: "Objetos" level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis level_tab_thangs_all: "Todo" level_tab_thangs_conditions: "Condiciones de inicio" level_tab_thangs_add: "Añadir Objetos" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" delete: "Borrar" duplicate: "Duplicar" +# stop_duplicate: "Stop Duplicate" rotate: "Rotar" level_settings_title: "Ajustes" level_component_tab_title: "Componentes Actuales" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis new_level_title_login: "Inicia sesión para Crear un Nuevo Nivel" new_achievement_title: "Crea un nuevo Logro" new_achievement_title_login: "Inicia sesión para Crear un Nuevo Logro" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" article_search_title: "Buscar artículos aquí" thang_search_title: "Busca tipos de objetos aquí" level_search_title: "Buscar niveles aquí" achievement_search_title: "Buscar Logros" +# poll_search_title: "Search Polls" read_only_warning2: "Nota: no puedes guardar nada de lo que edites aqui porque no has iniciado sesión." no_achievements: "No se han añadido logros a este nivel." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" level_completion: "Porcentaje de Nivel Completado" -# pop_i18n: "Populate I18N" + pop_i18n: "Poblar I18N" + tasks: "Tareas" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Vista preliminar" edit_article_title: "Editar artículo" +# polls: +# priority: "Priority" + contribute: page_title: "Colaborar" - character_classes_title: "Clases de Personajes" - introduction_desc_intro: "Tenemos muchas esperanzas en CodeCombat." - introduction_desc_pref: "Queremos estar donde programadores de todo tipo vengan a aprender y jugar juntos, introducir a otros en el maravilloso mundo de la programación y reflejar la mejor parte de la comunidad. No podemos, ni queremos, hacerlo solos; lo que hace grandes a proyectos como GitHub, Stack Overflow y Linux es la gente que los usa y crea con ellos. A tal fin, " - introduction_desc_github_url: "CodeCombat es totalmente de código abierto" - introduction_desc_suf: ", y nuestro objetivo es ofrecer tantas maneras como sea posible para que tomes parte y hagas de este proyecto algo tan tuyo como nuestro." - introduction_desc_ending: "¡Esperamos que te unas a nuestro equipo!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy y Matt" + intro_blurb: "¡CodeCombat es 100% código abierto! Cientos de dedicados jugadores nos han ayudado a convertir el juego en lo que es hoy en día. !Únete a nosotros y escribe el siguiente capítulo de CodeCombat en la aventura de enseñar al mundo a programar!" alert_account_message_intro: "¡Hola!" alert_account_message: "Para suscribirse a los mails de clase, necesitas estar logeado." - archmage_summary: "¿Interesado en trabajar en gráficos para juegos, el diseño de la interfaz de usuario, bases de datos y la organización de servidores, redes multijugador, físicas, sonido o el funcionamiento del motor del juego? ¿Quieres ayudar a construir un juego para ayudar a otras personas a aprender aquello en lo que eres bueno? Tenemos mucho que hacer y si eres un programador experimentado y quieres desarrollar para CodeCombat, esta clase es para tí. Nos encantaría recibir tu ayuda para construir el mejor juego de programación que se haya hecho." archmage_introduction: "Una de las mejores partes de desarrollar juegos es que combinan cosas muy diferentes. Gráficos, sonido, uso de redes en tiempo real, redes sociales y por supuesto mucho de los aspectos comunes de la programación, desde gestión de bases de datos a bajo nivel y administración de servidores hasta diseño de experiencia del usuario y creación de interfaces. Hay un montón de cosas por hacer y si eres un programador experimentado con interés en conocer lo que se cuece en la trastienda de CodeCombat, esta Clase puede ser la ideal para ti. Nos encantaría recibir tu ayuda para crear el mejor juego de programación de la historia." class_attributes: "Atributos de las Clases" archmage_attribute_1_pref: "Conocimiento en " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis join_desc_4: "¡y partiremos desde ese punto!" join_url_email: "Escríbenos un correo electrónico" join_url_hipchat: "sala pública en HipChat" - more_about_archmage: "Aprende más sobre convertirte en un poderoso Archimago" archmage_subscribe_desc: "Recibe correos sobre nuevos anuncios y oportunidades de codificar." - artisan_summary_pref: "¿Quieres diseñar niveles y ampliar el arsenal de CodeCombat? ¡La gente está jugando con nuestro contenido a un ritmo más rápido de lo que podemos construir! En este momento, nuestro editor de niveles está en una fase temprana, así que ten cuidado. Hacer niveles será un poco complicado y habrá errores. Si tienes en mente campañas fantásticas que lo abarquen todo" - artisan_summary_suf: ", entonces esta Clase es la tuya." artisan_introduction_pref: "¡Debemos construir niveles adicionales! La gente clama por más contenido y solo podemos crear unos cuantos. Ahora mismo tu estación de trabajo es el nivel uno; nuestro editor de niveles es apenas usable por sus creadores, así que ten cuidado. Si tienes visiones de campañas que alcanzan el infinito" artisan_introduction_suf: ", entonces esta Clase es ideal para ti." artisan_attribute_1: "Cualquier experiencia creando contenido similar estaría bien, como por ejemplo el editor de niveles de Blizzard. ¡Aunque no es necesaria!" @@ -645,47 +959,37 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis artisan_join_step2: "Crea un nuevo nivel y explora los niveles existentes." artisan_join_step3: "Busca nuestra sala pública de HipChat en busca de ayuda." artisan_join_step4: "Publica tus niveles en el foro para recibir comentarios críticos." - more_about_artisan: "Aprende más sobre convertirte en un Artesano creativo" artisan_subscribe_desc: "Recibe correos sobre actualizaciones del editor de niveles y anuncios." - adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." adventurer_introduction: "Hablemos claro sobre tu papel: eres el tanque. Vas a recibir fuertes daños. Necesitamos gente para probar nuestros flamantes niveles y ayudar a mejorarlos. El dolor será enorme; hacer buenos juegos es un proceso largo y nadie lo consigue a la primera. Si puedes resistir y tener una puntuación alta en Resistencia, entonces esta Clase es para ti." adventurer_attribute_1: "Estar sediento de conocimientos. Quieres aprender a programar y nosotros queremos enseñarte cómo hacerlo. Aunque en este caso es más probable que seas tú el que esté haciendo la mayor parte de la enseñanza." adventurer_attribute_2: "Carismático. Se amable pero claro a la hora de desglosar qué necesita ser mejorado y sugiere de qué formas podría hacerse." adventurer_join_pref: "Reúnete con (¡o recluta!) un Artesano y trabaja con ellos, o marca la casilla de abajo para recibir un correo cuando haya nuevos niveles para testar. También publicaremos en nuestras redes nuevos niveles para revisar" adventurer_forum_url: "nuestro foro" adventurer_join_suf: "así que si prefieres estar informado en esa forma, ¡crea una cuenta allí!" - more_about_adventurer: "Aprende más sobre cómo convertirte en un bravo Aventurero" adventurer_subscribe_desc: "Recibe correos cuando haya nuevos niveles para testar." - scribe_summary_pref: "CodeCombat no va a ser solo un conjunto de niveles. También será una fuente de conocimiento sobre programación a la que los jugadores podrán recurrir. De esa manera, cada Artesano puede enlazar a un artículo detallado que ayude al jugador: documentación afín a lo que el " - scribe_summary_suf: " ha escrito. Si disfrutas explicando conceptos de programación, entonces esta clase es para tí." scribe_introduction_pref: "CodeCombat no será solo un montón de niveles. También será una fuente de conocimientos, una wiki de conceptos de programación a la que los niveles se engancharan. De esa forma, en lugar de que cada Artesano tenga que describir en detalle qué es un operador de comparación, podrá simplemente enlazar el nivel al Artículo que los describe y que ya ha sido escrito para edificación del jugador. Algo en la línea de lo que la " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " ha construido. Si tu idea de diversión es articular los conceptos de la programación de una forma sencilla, entonces esta clase es para ti." scribe_attribute_1: "Habilidad a la hora de escribir es casi todo lo que necesitas. No solo dominar la gramática y la ortografía sino también expresar ideas complicadas a los demás de forma sencilla." contact_us_url: "Escribenos un correo electrónico" scribe_join_description: "cuéntanos más sobre ti, tu experiencia en el mundo de la programación y sobre qué cosas te gustaría escribir. ¡Y continuaremos a partir de ahí!" - more_about_scribe: "Aprende más sobre convertirte en un Escriba diligente" scribe_subscribe_desc: "Recibe correos sobre anuncios de redacción de Artículos." - diplomat_summary: "¡Hay un gran interés por CodeCombat en otros países que no hablan Inglés! Estamos buscando traductores que estén dispuestos a pasar su valioso tiempo traduciendo el corpus de palabras del sitio web para que CodeCombat sea accesible a todo el mundo tan pronto como sea posible. Si deseas ayudar para har de CodeCombat algo internacional, entonces esta clase es para tí." diplomat_introduction_pref: "Así, si hemos aprendido algo desde el " diplomat_launch_url: "lanzamiento en octubre" diplomat_introduction_suf: "hay un interés considerable en CodeCombat en otros paises, ¡especialmente Brasil! Estamos formando un cuerpo de traductores con ganas de traducir un grupo de palabras tras otro para hacer CodeCombat tan accesible para todo el mundo como sea posible. Si quieres recibir avances de próximos contenidos y quieres poner esos niveles a disposición de los que comparten tu idioma tan pronto como sea posible, entonces esta Clase es para ti." diplomat_attribute_1: "Fluidez con el ingles y el lenguaje al que quieras traducir. Cuando de transmitir ideas complejas se trata, ¡es importante tener grandes conocimientos de ambas!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." + diplomat_i18n_page_prefix: "Puedes traducir nuestros niveles yendo a nuestra" + diplomat_i18n_page: "página de traducciones" + diplomat_i18n_page_suffix: ", o en nuestra interfaz y sitio web en GitHub." diplomat_join_pref_github: "Encuentra el fichero local de tu idioma " diplomat_github_url: "en GitHub" diplomat_join_suf_github: ", edítalo online, y solicita que sea revisado. Además, marca la casilla de abajo para mantenerte informado en nuevos progresos en Internacionalización." - more_about_diplomat: "Aprende más sobre cómo convertirte en un gran Diplomático" diplomat_subscribe_desc: "Recibe correos sobre nuevos niveles y desarrollos para traducir." - ambassador_summary: "Estamos tratando de construir una comunidad, y cada comunidad necesita un equipo de apoyo para cuando hay problemas. Tenemos chats, correos electrónicos y redes sociales para que nuestros usuarios puedan familiarizarse con el juego. Si quieres ayudar a que la gente participe, se divierta y aprenda algo de programación, entonces esta clase es para tí." ambassador_introduction: "Esta es una comunidad en construcción y tú eres parte de las conexiones. Tenemos chat Olark, correos electrónicos y las redes sociales con una gran cantidad de personas con quienes hablar, ayudar a familiarizarse con el juego y aprender. Si quieres ayudar a la gente a que se involucre, se divierta, y tenga buenas sensaciones sobre CodeCombat y hacia dónde vamos, entonces esta clase es para ti." ambassador_attribute_1: "Habilidades de comunicación. Ser capaz de identificar los problemas que los jugadores están teniendo y ayudarles a resolverlos. Además, mantener al resto de nosotros informados sobre lo que los jugadores están diciendo, lo que les gusta, lo que no ¡y de lo que quieren más!" ambassador_join_desc: "cuéntanos más sobre ti, que has hecho y qué estarías interesado en hacer. ¡Y continuaremos a partir de ahí!" ambassador_join_note_strong: "Nota" ambassador_join_note_desc: "Una de nuestras principales prioridades es construir un modo multijugador donde los jugadores con mayores dificultades a la hora de resolver un nivel, puedan invocar a los magos más avanzados para que les ayuden. Será una buena manera de que los Embajadores puedan hacer su trabajo. ¡Te mantendremos informado!" - more_about_ambassador: "Aprende más sobre cómo convertirte en un amable Embajador" ambassador_subscribe_desc: "Recibe correos sobre actualizaciones de soporte y desarrollo del multijugador." changes_auto_save: "Los cambios son guardados automáticamente cuando marcas las casillas de verificación." diligent_scribes: "Nuestros diligentes Escribas:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis fight: "¡Pelea!" watch_victory: "Ver tu victoria" defeat_the: "Vence a" +# tournament_started: ", started" tournament_ends: "El torneo termina" tournament_ended: "El torneo ha terminado" tournament_rules: "Reglas del Torneo" tournament_blurb: "Escribe codigo, recolecta oro, construye ejercitos, aplasta a los malos, gana premios, y sube en tu carrera en nuestro Torneo de la Avaricia con $40,000! Ver los detalles" tournament_blurb_criss_cross: "Gana pujas, construye caminos, aniquila a tus oponentes, recoge gemas, y mejora tu carrera en nuestro torneo Criss-Cross! Mira los detalles" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" tournament_blurb_blog: "en nuestro blog" rules: "Reglas" winners: "Ganadores" @@ -763,14 +1069,15 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis no_achievements: "No has alcanzado ningun logro todavia." favorite_prefix: "Favorite language is " favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." achievements: last_earned: "Ganado la ultima vez" amount_achieved: "Cantidad" achievement: "Logro" category_contributor: "Contribuidor" -# category_ladder: "Ladder" -# category_level: "Level" + category_ladder: "Escalera" + category_level: "Nivel" category_miscellaneous: "Miscelanea" category_levels: "Niveles" category_undefined: "Sin categorizar" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis account: recently_played: "Jugado Recientemente" no_recent_games: "No he jugado juegos en las ultimas dos semanas." + payments: "Pagos" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" + service_apple: "Apple" + service_web: "Web" + paid_on: "Pagado en" + service: "Servicio" + price: "Precio" + gems: "Joyas" + active: "Activo" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" + cost: "Costo" + next_payment: "Siguiente Pago" + card: "Tarjeta" + status_unsubscribed_active: "No estás suscrito y no seras facturado, pero tu cuenta sigue activa por ahora." + status_unsubscribed: "¡Obten acceso a nuevos niveles, heroes, artículos, y joyas adicionales con una suscripción a CodeCombat!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "Error al cargar desde el servidor." @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis leaderboard: "Clasificación" user_schema: "Esquema de usuario" user_profile: "Perfil de usuario" +# patch: "Patch" patches: "Parches" patched_model: "Documento Fuente" model: "Modelo" @@ -825,7 +1162,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis opponent_session: "Sesión del oponente" article: "Artículo" user_names: "Nombres de usuarios" -# thang_names: "Thang Names" + thang_names: "Nombres Thang" files: "Archivos" top_simulators: "Top simuladores" source_document: "Documento fuente" @@ -834,24 +1171,51 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis employers: "Empleados" candidates: "Candidatos" candidate_sessions: "Sesiones de Candidato" -# user_remark: "User Remark" -# user_remarks: "User Remarks" + user_remark: "Observación de Usuario" + user_remarks: "Observaciones de Usuario" versions: "Versiones" items: "Objetos" +# hero: "Hero" heroes: "Heroes" - wizard: "Mago" achievement: "Logro" clas: "Clasess" play_counts: "Contador de Juegos" feedback: "Apoyo" + payment_info: "Información de Pago" +# campaigns: "Campaigns" + poll: "Encuesta" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" delta: added: "Añadido" modified: "Modificado" +# not_modified: "Not Modified" deleted: "Eliminado" moved_index: "Indice Movido" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" + text_diff: "Diferencias" + merge_conflict_with: "MERGE EN CONFLICTO CON" no_changes: "Sin Cambios" guide: @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis practices_title: "Prácticas respetuosas" practices_description: "Esto es lo que te prometemos a ti, el jugador, sin usar mucha jerga legal." privacy_title: "Privacidad" - privacy_description: "No venderemos tu información personal. Tenemos la intención de hacer dinero a través de la contratación con el tiempo, pero puedes estar seguro que no vamos a distribuir tu información personal a las empresas interesadas sin tu consentimiento expreso." + privacy_description: "No venderemos tu información personal." security_title: "Seguridad" security_description: "Nos esforzamos por mantener segura tu información personal. Como proyecto de código abierto, nuestro sitio está abierto a cualquiera que quiera revisarlo y mejorar nuestros sistemas de seguridad." email_title: "Correo electrónico" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis email_description_suffix: "o a través de los enlaces en los correos que te enviemos, puedes cambiar tus preferencias y darte de baja fácilmente en cualquier momento." cost_title: "Precio" cost_description: "Actualmente, ¡CodeCombat es 100% gratis! Uno de nuestros principales objetivos es mantenerlo así, de forma que el mayor número posible de gente pueda jugar, independientemente de sus posibilidades económicas. Si las cosas se tuercen, quizás tengamos que cobrar suscripciones o por algún contenido, pero preferimos no hacerlo. Con un poco de suerte, podremos mantener la empresa con: " - recruitment_title: "Contratación" - recruitment_description_prefix: "En CodeCombat, te vas a convertir en un poderoso mago no solo en el juego, también en el mundo real." - url_hire_programmers: "Nadie puede contratar programadores con la suficiente rapidez" - recruitment_description_suffix: "así que una vez que hayas afilado tus habilidades y si estás de acuerdo, mostraremos tus mejores logros en programación a los miles de empresas que están deseando tener la oportunidad de contratarte. Ellos nos pagan un poco y ellos te pagan a ti" - recruitment_description_italic: "un montón." - recruitment_description_ending: "La web permanece gratuita y todo el mundo es feliz. Ese es el plan." copyrights_title: "Copyrights y Licencias" contributor_title: "Acuerdo de Licencia del Colaborador" contributor_description_prefix: "Todas las colaboraciones, tanto en la web como en nuestro repositorio de GitHub, están sujetas a nuestro" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis license: "licencia" oreilly: "ebook de tu eleccion" - wizard_settings: - title: "Ajustes del mago" - customize_avatar: "Personaliza tu Avatar" - active: "Activo" - color: "Color" - group: "Grupo" - clothes: "Ropa" - trim: "Decoración" - cloud: "Nube" - team: "Equipo" - spell: "Hechizo" - boots: "Botas" - hue: "Matiz" - saturation: "Saturación" - lightness: "Brillo" - account_profile: settings: "Configuración" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Editar Perfil" @@ -986,7 +1328,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis next_long_description: "describe tu puesto de trabajo deseado." next_skills: "Pon al menos cinco habilidades." next_work: "Resume tu historia laboral." -# next_education: "recount your educational ordeals." + next_education: "Relatar tu experiencia educativa." next_projects: "Muestranos tres proyectos en los que hayas trabajado." next_links: "añade links personales o de redes sociales." next_photo: "añadir una foto para el perfil profesional." @@ -1001,7 +1343,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis basics_header: "Actualizar información básica" basics_active: "Abierto a ofertas" basics_active_help: "Buscas entrevistas de trabajo ahora?" -# basics_job_title: "Desired Job Title" + basics_job_title: "Nombre del Cargo deseado" basics_job_title_help: "Que tipo de rol estas buscando?" basics_city: "Ciudad" basics_city_help: "Ciudad en la que quieres trabajar (o en la que vives actualmente)." @@ -1024,11 +1366,11 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis # short_description: "Tagline" short_description_help: "¿Quién eres, y qué estás buscando? 140 caracteres como máximo." skills_header: "Habilidades" -# skills_help: "Tag relevant developer skills in order of proficiency." + skills_help: "Etiqueta tus habilidades de desarrolloen orden de competencia." long_description_header: "Describenos tu trabajo soñado" long_description_blurb: "Cuenta a tus empleadores lo impresionante que eres y que trabajo estas buscando." long_description: "Descripción Personal" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." + long_description_help: "Descríbete a ti mismo para tus potenciales empleadores. Mantenlo corto y directo. Recomendamos describir la posición que más te interesa. 600 caracteres max." work_experience: "Experiencia laboral" work_header: "Cuentanos un poco de tu historial laboral" work_years: "Años de experiencia" @@ -1036,15 +1378,15 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis work_blurb: "Haz un listado con la experiencia laboral relevante, los mas recientes primero." work_employer: "Empresa/Empleador" work_employer_help: "Nombre de la empresa /el empleador." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" + work_role: "Cargo" + work_role_help: "¿Cuál era tu cargo o rol?" work_duration: "Duración" # work_duration_help: "When did you hold this gig?" work_description: "Descripción" work_description_help: "Que has hecho alli? (140 caracteres maximo; opcional)" education: "Educación" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." + education_header: "Detalla tu experiencia académica" + education_blurb: "Lista tu experiencia académica." education_school: "Universidad" education_school_help: "Nombre de la Universidad." education_degree: "Titulo" @@ -1054,7 +1396,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis education_description: "Descripción" education_description_help: "Resalta algo de tu experiencia educacional. (140 caracteres maximo; optional)" our_notes: "Nuestras notas" -# remarks: "Remarks" + remarks: "Observaciones" projects: "Proyectos" projects_header: "Añade 3 proyectos" projects_header_2: "Proyectos (Top 3)" diff --git a/app/locale/fa.coffee b/app/locale/fa.coffee index 91556148a..a33dcfaa1 100644 --- a/app/locale/fa.coffee +++ b/app/locale/fa.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", slogan: "کد نویسیا با بازی بیاموزید" no_ie: "متاسفیم اما بازی بر روی مرورگر های اینترنت اکسپلورر نسخه ۹ به قبل اجرا نمی شود" # Warning that only shows up in IE8 and older no_mobile: "این بازی برای دستگاه های موبایل طراحی نشده است و بر روی آن ها اجرا نمی شود" # Warning that shows up on mobile devices - play: "شروع بازی" # The big play button that just starts playing a level + play: "شروع بازی" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" level_difficulty: "سختی: " campaign_beginner: "کمپین تازه کارها" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "مرحله خود را انتخاب کنید" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "شما می توانید به هرکدام از مراحل زیر پرش کنید یا ,در مورد آن مرحله بحث کنید" - adventurer_forum: "انجمن ماجراجو ها" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: ".که شما در آن می توانید جادوگری به وسیله برنامه نویسی را یادبگیرید..." - campaign_dev: "مراحل سخت تصادفی" - campaign_dev_description: "... جایی که میتونید طراحی ظاهر رو یاد بگیرید درحالی که فعالیت سخت تری انجام میدید" +# adjust_volume: "Adjust volume" campaign_multiplayer: "مسابقات چند نفره" campaign_multiplayer_description: "... جایی که کد رو به رو شدن با بقیه بازیکنان رو مینویسید." - campaign_player_created: "ایجاد بازیکن" - campaign_player_created_description: "... جایی که در مقابل خلاقیت نیرو هاتون قرار میگیرید <a href=\"/contribute#artisan\">جادوگران آرتیزان</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "ایجاد حساب کاربری" log_in: "ورود" # logging_in: "Logging In" log_out: "خروج" - recover: "بازیابی حساب کاربری" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" signup: - create_account_title: "ایجاد حساب کاربری برای ذخیره سازی پیشرفت ها" - description: ":ثبت نام رایگان است و برای شروع مقداری اطلاعات لازم داریم" email_announcements: "دریافت اطلاعیه ها توسط ایمیل" - coppa: "حداقل ۱۳ سال دارم یا امریکایی نیستم" - coppa_why: "(چرا؟)" creating: "...در حال ایجاد حساب کاربری" sign_up: "ثبت نام" log_in: "ورود به وسیله رمز عبور" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" recover: recover_account_title: "بازیابی حساب کاربری" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "...در حال بارگذاری" saving: "...در حال ذخیره سازی" sending: "...در حال ارسال" @@ -139,9 +140,14 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # fork: "Fork" play: "سطوح" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "ذخیره کردن نسخه جدید" new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." cla_prefix: "To save changes, first you must agree to our" cla_url: "CLA" cla_suffix: "." cla_agree: "موافقم" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "CodeCombatتماس با " welcome: "خوب است از شما بشنویم, از طریق این فرم برای ما ایمیل ارسال کنید" - contribute_prefix: "اگر علاقه مند به همکاری با ما هستید ، این صفحه را مطالعه کنید" - contribute_page: "صفحه همکاری" - contribute_suffix: "!" forum_prefix: "باری هر چیز عمومی، اینجا را مشاهده کنید " forum_page: "فاروم ما" forum_suffix: " به جای" +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "ارسال بازخورد" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", autosave: "تغییرات به طور خودکار ثبت شد" me_tab: "من" picture_tab: "تصاویر" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "کلمه عبور" emails_tab: "ایمیل ها" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" - wizard_tab: "جادو" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "تنظیمات جادویی" - customize_avatar: "آواتار خود را شکل دهید" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/fi.coffee b/app/locale/fi.coffee index 200e5e610..70aa64cb4 100644 --- a/app/locale/fi.coffee +++ b/app/locale/fi.coffee @@ -1,271 +1,301 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", translation: -# home: -# slogan: "Learn to Code by Playing a Game" + home: + slogan: "Opi Koodaamaan Pelaamalla" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level + play: "Pelaa" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." -# campaign: "Campaign" -# for_beginners: "For Beginners" -# multiplayer: "Multiplayer" # Not currently shown on home page -# for_developers: "For Developers" # Not currently shown on home page. +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." + campaign: "Kamppanja" + for_beginners: "Aloittelijoille" + multiplayer: "Moninpelaaja" # Not currently shown on home page + for_developers: "Kehittäjille" # Not currently shown on home page. # or_ipad: "Or download for iPad" -# nav: -# play: "Levels" # The top nav bar entry where players choose which levels to play -# community: "Community" -# editor: "Editor" -# blog: "Blog" -# forum: "Forum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + nav: + play: "Tasot" # The top nav bar entry where players choose which levels to play + community: "Yhteisö" + editor: "Editori" + blog: "Blogi" + forum: "Foorumi" + account: "Tili" + profile: "Profiili" + stats: "Tilastot" + code: "Koodi" # admin: "Admin" # Only shows up when you are an admin -# home: "Home" -# contribute: "Contribute" -# legal: "Legal" -# about: "About" -# contact: "Contact" -# twitter_follow: "Follow" -# teachers: "Teachers" + home: "Koti" + contribute: "Avusta" + legal: "Laillisuus" + about: "Tietoja" + contact: "Ota yhteyttä" + twitter_follow: "Seuraa" + teachers: "Opettajat" -# modal: -# close: "Close" -# okay: "Okay" + modal: + close: "Sulje" + okay: "Ok" -# not_found: -# page_not_found: "Page not found" + not_found: + page_not_found: "Sivua ei löydy" diplomat_suggestion: -# title: "Help translate CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. -# sub_heading: "We need your language skills." - pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Finnish but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Finnish." - missing_translations: "Until we can translate everything into Finnish, you'll see English when Finnish isn't available." -# learn_more: "Learn more about being a Diplomat" -# subscribe_as_diplomat: "Subscribe as a Diplomat" + title: "Auta kääntämään CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. + sub_heading: "Tarvitsemme apuasi kääntämään." + pitch_body: "CodeCombat on tehty englanninkielellä mutta pelaajia on jo ympäri maailmaa. Monet haluavat käyttää myös suomen kieltä. Jos haluat osallistua kääntämiseen ilmoittaudu Diplomaatiksi ja anna panoksesi." + missing_translations: "Siihen asti kunnes koko peli on käännetty suomeksi näet joitain tekstejä edelleen englanniksi." + learn_more: "Ota selvää mitä tarkoitta olla Diplomaatti" + subscribe_as_diplomat: "Tilaa Diplomaattina" -# play: -# play_as: "Play As" # Ladder page + play: + play_as: "Pelaa" # Ladder page # spectate: "Spectate" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" -# level_difficulty: "Difficulty: " -# campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." + players: "pelaajat" # Hover over a level on /play + hours_played: "pelatut tunnit" # Hover over a level on /play + items: "Esineet" # Tooltip on item shop button from /play + unlock: "Avaa" # For purchasing items and heroes + confirm: "Varmista" + owned: "Omistettu" # For items you own + locked: "Lukittu" + purchasable: "Ostettavissa" # For a hero you unlocked but haven't purchased + available: "Saatavissa" + skills_granted: "Taidot" # Property documentation details + heroes: "Sankarit" # Tooltip on hero shop button from /play + achievements: "Saavutukset" # Tooltip on achievement list button from /play + account: "Tili" # Tooltip on account button from /play + settings: "Asetukset" # Tooltip on settings button from /play + poll: "Kysely" # Tooltip on poll button from /play + next: "Seuraava" # Go from choose hero to choose inventory before playing a level + change_hero: "Vaihda sankaria" # Go back from choose inventory to choose hero + choose_inventory: "Varusta esineitä" + buy_gems: "Osta jalokiviä" + subscription_required: "Tilaus vaaditaan" + anonymous: "Nimetön Pelaaja" + level_difficulty: "Vaikeustaso: " + campaign_beginner: "Aloittelijan Kamppanja" +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." + adjust_volume: "Aänenvoimakkuus" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." -# login: -# sign_up: "Create Account" -# log_in: "Log In" -# logging_in: "Logging In" -# log_out: "Log Out" -# recover: "recover account" + share_progress_modal: + blurb: "Edistyt loistavasti! Kerro ystävillesi mitä olet oppinut CodeCombat:n avulla." + email_invalid: "Virheellinen sähköpostiosoite." + form_blurb: "Syötä heidän sähköpostiosoitteet ja me kerromme heille!" + form_label: "Sähköpostiosoite" + placeholder: "email" + title: "Erinomaista työtä, Oppipoika" + + login: + sign_up: "Luo Tili" + log_in: "Kirjaudu" + logging_in: "Kirjaudutaan" + log_out: "Kirjaudu Ulos" + forgot_password: "Unohditko salasanasi?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" + signup_switch: "Haluatko luoda tilin?" -# signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" -# email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" -# creating: "Creating Account..." -# sign_up: "Sign Up" -# log_in: "log in with password" + signup: + email_announcements: "Haluatko ilmoituksia sähköpostiisi" + creating: "Luodaan tili..." + sign_up: "Ilmoittaudu" + log_in: "kirjaudu sisään salasanalla" # social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." + required: "Kirjaudu ensin jotta pääset jatkamaan." + login_switch: "Onko sinulla jo tili?" # recover: # recover_account_title: "Recover Account" # send_password: "Send Recovery Password" # recovery_sent: "Recovery email sent." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Ensisijainen" + secondary: "Toissijainen" + armor: "Suoja" + accessories: "Tarvikkeet" + misc: "Sekalaista" + books: "Kirjat" common: - loading: "Loading..." -# saving: "Saving..." -# sending: "Sending..." -# send: "Send" -# cancel: "Cancel" -# save: "Save" -# publish: "Publish" -# create: "Create" -# manual: "Manual" -# fork: "Fork" -# play: "Play" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" + back: "Takaisin" # When used as an action verb, like "Navigate backward" + continue: "Jatka" # When used as an action verb, like "Continue forward" + loading: "Ladataan..." + saving: "Tallennetaan..." + sending: "Lähetetään..." + send: "Lähetä" + cancel: "Peruuta" + save: "Tallenna" + publish: "Julkaise" + create: "Luo" + manual: "Ohjeet" + fork: "Haarauta" + play: "Pelaa" # When used as an action verb, like "Play next level" + retry: "Kokeile uudestaan" + actions: "Toiminnot" + info: "Tietoa" + help: "Apua" + watch: "Seuraa" + unwatch: "Lopeta Seuraaminen" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" -# general: -# and: "and" -# name: "Name" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" -# or: "or" -# subject: "Subject" + general: + and: "ja" + name: "Nimi" + date: "Päiväys" + body: "Vartalo" + version: "Versio" + pending: "Odotetaan" + accepted: "Hyväksytty" + rejected: "Hylätty" + withdrawn: "Vedetty pois" + submitter: "Lähettäjä" + submitted: "Lähetetty" + commit_msg: "Lähetä Viesti" + review: "Esikatsele" + version_history: "Versiohistoria" + version_history_for: "Versiohistoria: " +# select_changes: "Select two changes below to see the difference." + undo_prefix: "Peruuta" +# undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Tee uudelleen" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" + result: "Tulos" + results: "Tulokset" + description: "Kuvaus" + or: "tai" + subject: "Aihe" # email: "Email" -# password: "Password" -# message: "Message" -# code: "Code" + password: "Salasana" + message: "Viesti" + code: "Koodi" # ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + when: "Kun" + opponent: "Vastustaja" + rank: "Arvo" + score: "Pisteet" + win: "Voitto" + loss: "Tappio" + tie: "Tasapeli" + easy: "Helppo" + medium: "Haastava" + hard: "Vaikea" + player: "Pelaaja" + player_level: "Taso" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Sotilas" + ranger: "Vartija" + wizard: "Velho" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "sekuntti" + seconds: "sekunnit" + minute: "minuutti" + minutes: "minuutit" + hour: "tunti" + hours: "tunnit" + day: "päivä" + days: "päivät" + week: "viikko" + weeks: "viikot" + month: "kuukausi" + months: "kuukaudet" + year: "vuosi" + years: "vuodet" -# play_level: -# done: "Done" + play_level: + done: "Valmis" # home: "Home" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" -# guide: "Guide" -# restart: "Restart" -# goals: "Goals" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" + level: "Taso" # Like "Level: Dungeons of Kithgard" + skip: "Ohita" + game_menu: "Pelivalikko" + guide: "Ohje" + restart: "Käynnistä uudelleen" + goals: "Tavoitteet" + goal: "Tavoite" + running: "Ajetaan..." + success: "Onnistui!" + incomplete: "Kesken" + timed_out: "Aika loppui" + failing: "Epäonnistui" # action_timeline: "Action Timeline" -# click_to_select: "Click on a unit to select it." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" -# reload_title: "Reload All Code?" -# reload_really: "Are you sure you want to reload this level back to the beginning?" -# reload_confirm: "Reload All" + click_to_select: "Jos valitset yksikön, klikkaa sitä." + control_bar_multiplayer: "Moninpelaaja" + control_bar_join_game: "Liity peliin" + reload: "Lataa" + reload_title: "Ladataanko kaikki koodi uudestaan?" + reload_really: "Oletko varma että haluat ladata tämän tason uudestaan ja aloittaa alusta?" + reload_confirm: "Lataa Kaikki" + victory: "Voitto" # victory_title_prefix: "" -# victory_title_suffix: " Complete" -# victory_sign_up: "Sign Up to Save Progress" -# victory_sign_up_poke: "Want to save your code? Create a free account!" + victory_title_suffix: " Valmis" + victory_sign_up: "Kirjaudu" + victory_sign_up_poke: "Haluatko tallettaa koodisi? Luo ilmainen tili!" # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "Jatka" + victory_saving_progress: "Tallennetaan edistymisesi" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. -# victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" -# guide_title: "Guide" + victory_hour_of_code_done: "Oletko Valmis?" + victory_hour_of_code_done_yes: "Kyllä, Koodituntini on vamis!" + victory_experience_gained: "Kokemusta" + victory_gems_gained: "Jalokiviä" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" + guide_title: "Opas" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. # tome_other_units: "Other Units" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). -# tome_select_a_thang: "Select Someone for " -# tome_available_spells: "Available Spells" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" + tome_cast_button_run: "Suorita" + tome_cast_button_running: "Suoritetaan" + tome_cast_button_ran: "Suoritettu" + tome_submit_button: "Lähetä" + tome_reload_method: "Lataa koodi uudelleen" # Title text for individual method reload button. + tome_select_method: "Valitse metodi" + tome_see_all_methods: "Kaikki muokattavat metodit" # Title text for method list selector (shown when there are multiple programmable methods). + tome_select_a_thang: "Valitse hahmo tälle esineelle " + tome_available_spells: "Saatavissa olevat taiat" + tome_your_skills: "Taitosi" + tome_help: "Apua" + tome_current_method: "Vaaditut toimet" + hud_continue_short: "Jatka" + code_saved: "Koodi on tallennettu" + skip_tutorial: "Ohita (esc)" + keyboard_shortcuts: "Pikanäppäimet" + loading_ready: "Valmis!" + loading_start: "Aloita Taso" + problem_alert_title: "Korjaa koodisi" + problem_alert_help: "Apua" + time_current: "Nyt:" + time_total: "Enintään:" + time_goto: "Mene:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "Yritä uudelleen" + infinite_loop_reset_level: "Aloita taso uudelleen" + infinite_loop_comment_out: "Kommentoi koodirivejä" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." + tip_open_source: "CodeCombat on 100% avointa lähdekoodia!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat avattiin betatestaukseen lokakuussa 2013." + tip_think_solution: "Mieti ratkaisua, älä ongelmaa." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" # tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" # tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" @@ -289,114 +319,234 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." -# game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" -# multiplayer_tab: "Multiplayer" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + game_menu: + inventory_tab: "Luettelo" + save_load_tab: "Tallenna/Lataa" + options_tab: "Asetukset" + guide_tab: "Opas" + guide_video_tutorial: "Video-oppaat" + guide_tips: "Vinkkejä" + multiplayer_tab: "Moninpelaaja" + auth_tab: "Kirjaudu" + inventory_caption: "Varusta sankari" + choose_hero_caption: "Valitse sankari ja kieli" + save_load_caption: "... ja katso historiaa" + options_caption: "Muuta asetuksia" + guide_caption: "Ohjeita ja vinkkejä" + multiplayer_caption: "Pelaa ystävien kanssa!" + auth_caption: "Tallenna edistymisesi." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Pistetaulu" + view_other_solutions: "Katso Muita Ratkaisuja" # {change} + scores: "Pisteet" + top_players: "Parhaat pelaajat" + day: "Tänään" + week: "Tällä Viikolla" + all: "Koskaan" + time: "Aika" + damage_taken: "Vanhinkoa saatu" + damage_dealt: "Vahinkoa annettu" + difficulty: "Vaikeustaso" + gold_collected: "Kultaa kerätty" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." + inventory: + choose_inventory: "Ota Käyttöön" + equipped_item: "Käytössä" + required_purchase_title: "Vaadittu" + available_item: "Saatavilla" + restricted_title: "Rajoitettu" + should_equip: "(ota käyttöön tuplaklikkaamalla)" + equipped: "(käytössä)" + locked: "(lukittu)" + restricted: "(ei saatavilla tässä kentässä)" + equip: "Ota käyttöön" + unequip: "Ota pois käytöstä" + + buy_gems: + few_gems: "Muutama jalokivi" + pile_gems: "Kasa jalokiviä" + chest_gems: "Arkullinen jalokiviä" + purchasing: "Ostetaan..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" + free: "Ilmainen" + month: "kuukausi" + subscribe_title: "Tilaa" + unsubscribe: "Poista tilaus" + confirm_unsubscribe: "Varmista tilauksen poisto" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" + parent_button: "Kysy vanhemmiltasi" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" + + choose_hero: + choose_hero: "Valitse Sankarisi" + programming_language: "Ohjelmointikieli" + programming_language_description: "Mitä ohjelmointikieltä haluat käyttää?" + default: "Oletus" + experimental: "Kokeellinen" + python_blurb: "Yksinkertainen mutta voimakas, loistava niin aloittelijoille kuin experteille." + javascript_blurb: "Netin kieli. (Ei ole sama kuin Java.)" + coffeescript_blurb: "Kivempi JavaScript kirjoitusmuoto." + clojure_blurb: "Moderni Lisp." + lua_blurb: "Pelien skriptauskieli." + io_blurb: "Yksinkertainen mutta hämärä." # status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + hero_type: "Tyyppi" + weapons: "Aseet" + weapons_warrior: "Miekat - Lyhyt kantama, Ei taikuutta" + weapons_ranger: "Jousipyssy, Aseet - Pitkä kantama, Ei taikuutta" + weapons_wizard: "Sauvat, Kepit - Pitkä kantana, Osaa taikoa" + attack: "Hyökkays" # Can also translate as "Attack" + health: "Terveys" + speed: "Nopeus" + regeneration: "Palautumisnopeus" + range: "Etäisyys" # As in "attack or visual range" + blocks: "Esto" # As in "this shield blocks this much damage" + backstab: "Selkäänpuukotus" # As in "this dagger does this much backstab damage" + skills: "Taidot" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + skill_docs: + writable: "kirjoitettava" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "vain luettava" + action_name: "nimi" + action_cooldown: "Kesto" + action_specific_cooldown: "Jäähdytys" + action_damage: "Vahinko" + action_range: "Etäisyys" + action_radius: "Säde" + action_duration: "Kesto" + example: "Esimerkki" + ex: "esim." # Abbreviation of "example" + current_value: "Nykyinen Arvo" + default_value: "Oletusarvo" + parameters: "Parametrit" + returns: "Palauttaa" + granted_by: "Saatu" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + save_load: + granularity_saved_games: "Tallennettu" + granularity_change_history: "Historia" -# options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." + options: + general_options: "Yleiset Asetukset" # Check out the Options tab in the Game Menu while playing a level + volume_label: "Äänenvoimakkuus" + music_label: "Musiikki" + music_description: "Aseta taustamusiikki päälle/pois päältä." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" # editor_config_level_language_description: "Define the programming language for this particular level." # editor_config_default_language_label: "Default Programming Language" # editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" -# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." + editor_config_keybindings_label: "Näppäimistö" + editor_config_keybindings_default: "Oletus (Ace)" + editor_config_keybindings_description: "Lisää uusia pikanäppäimiä jotka ovat käytössä valitussa editorissa." + editor_config_livecompletion_label: "Automaattinen koodin täydennys" + editor_config_livecompletion_description: "Näyttää ehdotuksen koodin kirjoittamisen yhteydessä." + editor_config_invisibles_label: "Näytä Näkymättömät Merkit" + editor_config_invisibles_description: "Näyttää myös näkymättömät merkit kuten välilyönti ja tabulaattori." + editor_config_indentguides_label: "Näytä Sisennysehdotukset" + editor_config_indentguides_description: "Näyttää vertikaalisen linjan helpottamaan sisennystä." + editor_config_behaviors_label: "Älykäät Toiminnot" + editor_config_behaviors_description: "Täydentää automaattisesti sulut ja lainausmerkit." # about: # why_codecombat: "Why CodeCombat?" @@ -412,66 +562,167 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated -# account_settings: -# title: "Account Settings" -# not_logged_in: "Log in or create an account to change your settings." -# autosave: "Changes Save Automatically" -# me_tab: "Me" -# picture_tab: "Picture" -# upload_picture: "Upload a picture" -# password_tab: "Password" -# emails_tab: "Emails" + account_settings: + title: "Tiliasetukset" + not_logged_in: "Kirjaudu sisään tai luo tili jotta pelitilanne voidaan tallentaa." + autosave: "Tallenna Muutokset Automaattisesti" + me_tab: "Minä" + picture_tab: "Kuva" + delete_account_tab: "Poista Tili" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" + upload_picture: "Lataa kuva" + delete_this_account: "Poista tili lopullisesti" +# god_mode: "God Mode" + password_tab: "Salasana" + emails_tab: "Sähköpostitiedotteet" # admin: "Admin" -# new_password: "New Password" -# new_password_verify: "Verify" + new_password: "Uusi Salasana" + new_password_verify: "Varmista" + type_in_email: "Anna sähköpostiosoitteesi varmistukseksi" # {change} +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." -# email_announcements: "Announcements" -# email_announcements_description: "Get emails on the latest news and developments at CodeCombat." -# email_notifications: "Notifications" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." -# contributor_emails: "Contributor Class Emails" -# contribute_prefix: "We're looking for people to join our party! Check out the " -# contribute_page: "contribute page" -# contribute_suffix: " to find out more." -# email_toggle: "Toggle All" + email_announcements: "Tiedotteet" + email_announcements_description: "Haluatko viimeisimmät uutiset ja tiedoteet uusista ominaisuuksista." + email_notifications: "Muistutukset" + email_notifications_summary: "Säätää personoituja ja automaattisia sähköpostimuistutuksia aktiviteeteistasi sivustolla." + email_any_notes: "Kaikki Muistutukset" + email_any_notes_description: "Poista jos et halua muistutuksia sähköpostiisi." + email_news: "Uutiset" + email_recruit_notes: "Työmahdollisuudet" + email_recruit_notes_description: "Jos pelaat todella hyvin saatamme ottaa sinuun yhteyttä ja tarjota työmahdollisuuksia." + contributor_emails: "Avustajakohtaiset Tiedotteet" + contribute_prefix: "Etsimme lisää tekijöitä! Katso lisää " + contribute_page: "avusta sivulta" + contribute_suffix: " ja lue miten sinäkin pääset mukaan." + email_toggle: "Valitse/Poista Kaikki" # error_saving: "Error Saving" # saved: "Changes Saved" # password_mismatch: "Password does not match." @@ -481,17 +732,16 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" + keyboard_shortcuts: + keyboard_shortcuts: "Pikanäppäimet" + space: "Välilyönti" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." + run_code: "Suorita koodi." + run_real_time: "Suorita tosiajassa." # continue_script: "Continue past current script." # skip_scripts: "Skip past all skippable scripts." # toggle_playback: "Toggle play/pause." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" -# classes: -# archmage_title: "Archmage" -# archmage_title_description: "(Coder)" -# artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" -# scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" -# diplomat_title_description: "(Translator)" -# ambassador_title: "Ambassador" -# ambassador_title_description: "(Support)" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + + classes: + archmage_title: "Arkkimaagi" + archmage_title_description: "(Koodaaja)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" + artisan_title: "Käsityöläinen" + artisan_title_description: "(Kenttäsuunnittelija)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." + adventurer_title: "Seikkailija" + adventurer_title_description: "(Kenttätestaaja)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." + scribe_title: "Kirjuri" + scribe_title_description: "(Toimittaja)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." + diplomat_title: "Diplomaatti" + diplomat_title_description: "(Kääntäjä)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." + ambassador_title: "Suurlähettiläs" + ambassador_title_description: "(Tuki)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" + polls: + priority: "Prioriteetti" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,29 +1044,32 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" + user: + stats: "Tilastot" + singleplayer_title: "Yksinpelaajan Kenttiä" + multiplayer_title: "Moninpelaajan Kenttiä" + achievements_title: "Saavutukset" + last_played: "Viimeksi Pelattu" # status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" + status_completed: "Valmis" + status_unfinished: "Kesken" # no_singleplayer: "No Singleplayer games played yet." # no_multiplayer: "No Multiplayer games played yet." # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/fr.coffee b/app/locale/fr.coffee index 6ba8ff7ef..a37e53dbe 100644 --- a/app/locale/fr.coffee +++ b/app/locale/fr.coffee @@ -2,15 +2,16 @@ module.exports = nativeDescription: "français", englishDescription: "French", t home: slogan: "Apprenez à coder tout en jouant" no_ie: "CodeCombat ne fonctionnera pas sous Internet Explorer 8 ou moins. Désolé !" # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat n'a pas été créé pour les plateformes mobiles donc il est possible qu'il ne fonctionne pas correctement ! " # Warning that shows up on mobile devices - play: "Jouer" # The big play button that just starts playing a level - old_browser: "Oh oh, votre navigateur est trop vieux pour executer CodeCombat. Désolé!" # Warning that shows up on really old Firefox/Chrome/Safari - old_browser_suffix: "Vous pouvez essayer quand même, mais celà ne marchera probablement pas." + no_mobile: "CodeCombat n'a pas été créé pour les plateformes mobiles, il est donc possible qu'il ne fonctionne pas correctement !" # Warning that shows up on mobile devices + play: "Jouer" # The big play button that opens up the campaign view. + old_browser: "Oh oh, votre navigateur est trop vieux pour exécuter CodeCombat. Désolé !" # Warning that shows up on really old Firefox/Chrome/Safari + old_browser_suffix: "Vous pouvez essayer quand même, mais cela ne marchera probablement pas." + ipad_browser: "Mauvaise nouvelle : CodeCombat ne fonctionne pas avec le navigateur iPad. Bonne nouvelle : notre application est en cours de validation par Apple !" campaign: "Campagne" for_beginners: "Pour débutants" multiplayer: "Multijoueurs" # Not currently shown on home page for_developers: "Pour développeurs" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Ou télécharger pour iPad" nav: play: "Jouer" # The top nav bar entry where players choose which levels to play @@ -20,8 +21,8 @@ module.exports = nativeDescription: "français", englishDescription: "French", t forum: "Forum" account: "Compte" profile: "Profil" -# stats: "Stats" -# code: "Code" + stats: "Stats" + code: "Code" admin: "Admin" # Only shows up when you are an admin home: "Accueil" contribute: "Contribuer" @@ -39,79 +40,77 @@ module.exports = nativeDescription: "français", englishDescription: "French", t page_not_found: "Page non trouvée" diplomat_suggestion: - title: "Aidez à traduire CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. + title: "Aidez à traduire CodeCombat !" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "Nous avons besoin de vos compétences en langues." - pitch_body: "Nous développons CodeCombat en Anglais, mais nous avons déjà des joueurs de partout dans le monde. Beaucoup d'entre eux veulent jouer en Français mais ne parlent pas l'anglais, donc si vous parlez aussi bien l'anglais que le français, aidez-nous en devenant traducteur et aidez à traduire aussi bien le site que tous les niveaux en français." + pitch_body: "Nous développons CodeCombat en anglais, mais nous avons déjà des joueurs de partout dans le monde. Beaucoup d'entre eux veulent jouer en français mais ne parlent pas anglais, donc si vous parlez aussi bien l'anglais que le français, aidez-nous en devenant traducteur et aidez à traduire aussi bien le site que tous les niveaux en français." missing_translations: "Jusqu'à ce que nous ayons tout traduit en français, vous verrez de l'anglais quand le français ne sera pas disponible." - learn_more: "Apprenez en plus sur les Traducteurs" + learn_more: "Apprenez-en plus sur les traducteurs" subscribe_as_diplomat: "S'inscrire en tant que traducteur" play: play_as: "Jouer comme " # Ladder page spectate: "Spectateur" # Ladder page - players: "joueurs" # Hover over a level on /play - hours_played: "heures jouées" # Hover over a level on /play + players: "Joueurs" # Hover over a level on /play + hours_played: "Heures jouées" # Hover over a level on /play items: "Objets" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details + unlock: "Déverrouiller" # For purchasing items and heroes + confirm: "Confirmer" + owned: "Acquis" # For items you own + locked: "Verrouillé" + purchasable: "Achetable" # For a hero you unlocked but haven't purchased + available: "Disponible" + skills_granted: "Compétences acquises" # Property documentation details heroes: "Héros" # Tooltip on hero shop button from /play achievements: "Succès" # Tooltip on achievement list button from /play account: "Compte" # Tooltip on account button from /play settings: "Réglages" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play next: "Suivant" # Go from choose hero to choose inventory before playing a level - change_hero: "Changer le Héro" # Go back from choose inventory to choose hero + change_hero: "Changer le héros" # Go back from choose inventory to choose hero choose_inventory: "Équiper des objets" -# buy_gems: "Buy Gems" - older_campaigns: "Anciennes Campagnes" + buy_gems: "Acheter des gemmes" + subscription_required: "Enregistrement nécessaire" anonymous: "Joueur anonyme" - level_difficulty: "Difficulté: " + level_difficulty: "Difficulté : " campaign_beginner: "Campagne du Débutant" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Choisissez votre niveau" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Vous pouvez passer à n'importe quel niveau ci-dessous, ou discuter des niveaux sur " - adventurer_forum: "le forum de l'Aventurier" - adventurer_suffix: "." - campaign_old_beginner: "Anciennes Campagnes pour Débutant" - campaign_old_beginner_description: "... dans laquelle vous apprendrez la magie de la programmation." - campaign_dev: "Niveaux aléatoires plus difficiles" - campaign_dev_description: "... dans lesquels vous apprendrez à utiliser l'interface en faisant quelque chose d'un petit peu plus dur." + awaiting_levels_adventurer_prefix: "Nous produisons cinq niveaux par semaine." # {change} + awaiting_levels_adventurer: "S'inscrire comme aventurier" + awaiting_levels_adventurer_suffix: "afin d'être le premier à jouer de nouveaux niveaux." + adjust_volume: "Ajuster le volume" campaign_multiplayer: "Campagne multi-joueurs" - campaign_multiplayer_description: "... dans laquelle vous coderez en face à face contre d'autres joueurs." - campaign_player_created: "Niveaux créés par les joueurs" - campaign_player_created_description: "... Dans laquelle vous serez confrontés à la créativité des votres.<a href=\"/contribute#artisan\">Artisan Wizards</a>." - campaign_classic_algorithms: "Algorithmes classiques" - campaign_classic_algorithms_description: "... dans lesquels vous apprendrez les algorithmes les plus populaires dans l'informatique." - campaign_forest: "Campagne de la forêt" - campaign_dungeon: "Compagne du donjon" + campaign_multiplayer_description: "... dans laquelle vous coderez en face-à-face contre d'autres joueurs." + campaign_old_multiplayer: "(Obsolète) Ancienne arènes multijoueurs" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Vous faites beaucoup de progrès ! Partagez ce que vous avez appris grâce à CodeCombat avec vos amis." # {change} + email_invalid: "Adresse e-mail non valide" + form_blurb: "Entres leur adresse e-mail ci-dessous, on leur montrera !" + form_label: "Adresse e-mail" + placeholder: "adresse e-mail" + title: "Très bon travail apprenti !" login: sign_up: "Créer un compte" log_in: "Connexion" logging_in: "Connecter" log_out: "Déconnexion" - recover: "Récupérer son compte" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Mot de passe oublié ?" + authenticate_gplus: "Authentication Google+" + load_profile: "Charger profil Google+" + finishing: "Finalisation" + sign_in_with_facebook: "Connecter avec Facebook" + sign_in_with_gplus: "Connecter avec Google+" + signup_switch: "Voulez-vous créer un compte ?" signup: - create_account_title: "Créer un compte pour sauvegarder votre progression" - description: "C'est gratuit. Simplement quelques informations et vous pourrez commencer :" - email_announcements: "Recevoir les annonces par email" - coppa: "13+ ou hors É-U" - coppa_why: "(Pourquoi?)" + email_announcements: "Recevoir les annonces par e-mail" creating: "Création du compte en cours..." sign_up: "S'abonner" log_in: "se connecter avec votre mot de passe" - social_signup: "Ou, vous pouvez vous identifier avec Facecook ou G+:" + social_signup: "Ou, vous pouvez vous identifier avec Facecook ou Google+ :" required: "Vous devez être connecté pour voir cela" + login_switch: "Avez-vous déjà un compte ?" recover: recover_account_title: "Récupérer son compte" @@ -119,14 +118,16 @@ module.exports = nativeDescription: "français", englishDescription: "French", t recovery_sent: "Email de récupération envoyé" items: -# primary: "Primary" -# secondary: "Secondary" + primary: "Primaire" + secondary: "Secondaire" armor: "Armure" accessories: "Accessoires" misc: "Divers" -# books: "Books" + books: "Livres" common: + back: "Retour" # When used as an action verb, like "Navigate backward" + continue: "Continuer" # When used as an action verb, like "Continue forward" loading: "Chargement..." saving: "Sauvegarde..." sending: "Envoi..." @@ -139,25 +140,43 @@ module.exports = nativeDescription: "français", englishDescription: "French", t fork: "Fork" play: "Jouer" # When used as an action verb, like "Play next level" retry: "Reessayer" + actions: "Actions" + info: "Info" + help: "Aide" watch: "Regarder" unwatch: "Ne plus regarder" submit_patch: "Soumettre un correctif" + submit_changes: "Soumettre des Changements" + save_changes: "Sauvegarder les modifications" general: and: "et" name: "Nom" -# date: "Date" + date: "Date" body: "Corps" version: "Version" + pending: "En attente" + accepted: "Accepté" + rejected: "Rejeté" + withdrawn: "Retiré" + submitter: "Soumissionnaire" + submitted: "Soumis" commit_msg: "Message de mise à jour" + review: "Examen" version_history: "Historique des versions" version_history_for: "Historique des versions pour : " - result: "Resultat" + select_changes: "Sélectionner deux changements plus bas pour voir la différence." + undo_prefix: "Annuler" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Refaire" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Jouer un aperçu du niveau actuel" + result: "Résultat" results: "Résultats" description: "Description" or: "ou" subject: "Sujet" - email: "Email" + email: "E-mail" password: "Mot de passe" message: "Message" code: "Code" @@ -173,7 +192,10 @@ module.exports = nativeDescription: "français", englishDescription: "French", t medium: "Moyen" hard: "Difficile" player: "Joueur" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Niveau" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Guerrier" + ranger: "Ranger" + wizard: "Magicien" units: second: "seconde" @@ -194,180 +216,310 @@ module.exports = nativeDescription: "français", englishDescription: "French", t play_level: done: "Fait" home: "Accueil" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" + level: "Niveau" # Like "Level: Dungeons of Kithgard" skip: "Passer" game_menu: "Menu du jeu" guide: "Guide" restart: "Relancer" goals: "Objectifs" goal: "Objectif" -# running: "Running..." + running: "en cours..." success: "Succès" incomplete: "Incomplet" timed_out: "Plus de temps" failing: "Échec" action_timeline: "Action sur la ligne de temps" click_to_select: "Clique sur une unité pour la sélectionner." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" - reload_title: "Recharger tout le code?" - reload_really: "Êtes-vous sûr de vouloir recharger ce niveau et retourner au début?" - reload_confirm: "Tout recharger" + control_bar_multiplayer: "Multi-joueurs" + control_bar_join_game: "Rejoindre la partie" + reload: "Recommencer" + reload_title: "Recommencer tout le code ?" + reload_really: "Êtes-vous sûr de vouloir recommencer ce niveau et retourner au début ?" + reload_confirm: "Tout recommencer" + victory: "Victoire" victory_title_prefix: "" victory_title_suffix: " Terminé" victory_sign_up: "Inscrivez-vous pour recevoir les mises à jour" - victory_sign_up_poke: "Vous voulez recevoir les dernières actualités par mail? Créez un compte gratuitement et nous vous tiendrons informés!" + victory_sign_up_poke: "Vous voulez recevoir les dernières actualités par e-mail ? Créez un compte gratuitement et nous vous tiendrons informés !" victory_rate_the_level: "Notez ce niveau: " # Only in old-style levels. victory_return_to_ladder: "Retourner à l'échelle" victory_play_continue: "Continuer" - victory_play_skip: "Passer" - victory_play_next_level: "Jouer au prochain niveau" - victory_play_more_practice: "Plus de pratique" - victory_play_too_easy: "Trop Facile" - victory_play_just_right: "Juste" - victory_play_too_hard: "Trop Dur" victory_saving_progress: "Sauvegarder la progression" victory_go_home: "Retourner à l'accueil" # Only in old-style levels. - victory_review: "Dites-nous en plus!" # Only in old-style levels. + victory_review: "Dites-nous en plus !" # Only in old-style levels. victory_hour_of_code_done: "Déjà fini ?" - victory_hour_of_code_done_yes: "Oui, j'ai fini mon heure de code!" + victory_hour_of_code_done_yes: "Oui, j'ai fini mon heure de code !" + victory_experience_gained: "XP gagnée" + victory_gems_gained: "Gemmes gagnées" + victory_new_item: "Nouvel item" + victory_viking_code_school: "Par la barbe d'Odin, c'est un niveau difficile que tu viens de compléter! Si tu n'est pas un développeur de logiciel, tu devrais l'être ! Ceci vient de te propulser dans l'école de Code Vikings où tu pourras amener tes habilités au prochain niveau et devenir un développer web profesionnel en deux semaines." + victory_become_a_viking: "Devenez un viking" guide_title: "Guide" tome_minion_spells: "Les sorts de vos soldats" # Only in old-style levels. tome_read_only_spells: "Sorts en lecture-seule" # Only in old-style levels. tome_other_units: "Autres unités" # Only in old-style levels. tome_cast_button_run: "Exécuter" tome_cast_button_running: "En cours d'exécution" - tome_cast_button_ran: "Exécuté" + tome_cast_button_ran: "Exécuter" tome_submit_button: "Envoyer" tome_reload_method: "Recharger le code original pour cette méthode" # Title text for individual method reload button. tome_select_method: "Selectionner une méthode" - tome_see_all_methods: "Voir toutes les méthodes que vous pouvez éditer" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "Voir toutes les méthodes que vous pouvez éditer" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Sélectionnez une unité pour" tome_available_spells: "Sorts diponibles" tome_your_skills: "Vos compétences" -# tome_current_method: "Current Method" + tome_help: "Aide" + tome_current_method: "Méthode actuelle" hud_continue_short: "Continuer" - code_saved: "Code Sauvergardé" + code_saved: "Code sauvergardé" skip_tutorial: "Passer (esc)" - keyboard_shortcuts: "Raccourcis Clavier" + keyboard_shortcuts: "Raccourcis clavier" loading_ready: "Pret!" - loading_start: "Démarrer le niveau" -# problem_alert_title: "Fix Your Code" - time_current: "Maintenant:" - time_total: "Max:" - time_goto: "Allez a:" - infinite_loop_try_again: "Reessayer" + loading_start: "Démarrer niveau" + problem_alert_title: "Corriger votre code" + problem_alert_help: "Aide" + time_current: "Maintenant :" + time_total: "Max :" + time_goto: "Allez à :" + non_user_code_problem_title: "Chargement du niveau impossible" + infinite_loop_title: "Détection d'une boucle infinie" + infinite_loop_description: "Le code initial pour initialiser le monde n'a jamais terminé de s'exécuter. Soit la connection internet est trop lente ou soit il y a une présence d'une boucle infinie. Peut-être il s'agit d'un bug. Essayez de soit relancer le code à nouveau ou reinitialiser le code à son état par défaut. Si la situation ne change pas, veuillez nous aviser." + check_dev_console: "Vous pouvez ouvrir la console du développeur et tenter d'observer ce qui se passe qui causerait un souci." + check_dev_console_link: "(instructions)" + infinite_loop_try_again: "Réessayer" infinite_loop_reset_level: "Redémarrer le niveau" - infinite_loop_comment_out: "Supprimez les commentaire de mon code" + infinite_loop_comment_out: "Supprimez les commentaires de mon code" tip_toggle_play: "Jouer/Pause avec Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rembobinage et avance rapide." - tip_guide_exists: "Cliquez sur le guide en au haut de la page pour des informations utiles." - tip_open_source: "CodeCombat est 100% open source!" + tip_scrub_shortcut: "Ctrl+[ and Ctrl+] : rembobinage et avance rapide." # {change} + tip_guide_exists: "Cliquez sur le guide en haut de la page pour des informations utiles." + tip_open_source: "CodeCombat est 100% open source !" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "La beta de CodeCombat a été lancée en Octobre 2013" - tip_think_solution: "Reflechissez a propos de la solution et non du problème." - tip_theory_practice: "En théorie, il n'y a pas de différence entre la théorie et la pratique. Mais en pratique il y en a. - Yogi Berra" + tip_think_solution: "Reflechissez à propos de la solution et non du problème." + tip_theory_practice: "En théorie, il n'y a pas de différence entre la théorie et la pratique. Mais en pratique, il y en a. - Yogi Berra" tip_error_free: "Il ya deux façons d'écrire des programmes sans erreur; seulement la troisième marche. - Alan Perlis" - tip_debugging_program: "Si débugger est l'art de corriger les bugs, alors programmer est l'art d'en créer. . - Edsger W. Dijkstra" - tip_forums: "Rendez-vous sur le forum et dites nous ce que vous en pensez!" + tip_debugging_program: "Si débugger est l'art de corriger les bugs, alors programmer est l'art d'en créer. - Edsger W. Dijkstra" + tip_forums: "Rendez-vous sur le forum et dites-nous ce que vous en pensez !" tip_baby_coders: "Dans le futur, même les bébés seront des archimages." tip_morale_improves: "Le chargement se poursuivra jusqu'à ce que la morale s'améliore." tip_all_species: "Nous croyons en l'égalité des chances d'apprendre la programmation pour toutes les espèces." -# tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " + tip_reticulating: "Réticulation des spines." + tip_harry: "T'es un magicien, " tip_great_responsibility: "Avec de grandes compétences en programmation vient une grande responsabilité de débogage." tip_munchkin: "Si vous ne mangez pas vos légumes, un munchkin viendra après vous pendant que vous dormirez." tip_binary: "Il ya seulement 10 types de personnes dans le monde: ceux qui comprennent le binaire, et ceux qui ne le comprennent pas." - tip_commitment_yoda: " Un programmeur doit avoir l'engagement le plus profond, l'esprit le plus grave. ~ Yoda" + tip_commitment_yoda: "Un programmeur doit avoir l'engagement le plus profond, l'esprit le plus grave. ~ Yoda" tip_no_try: "Faire. Ou ne pas faire. Il n'y a pas d'essai. - Yoda" tip_patience: "Patience tu dois avoir, jeune Padawan. - Yoda" tip_documented_bug: "Un bug documenté n'est pas un bug; c'est une caractéristique" tip_impossible: "Cela semble toujours impossible jusqu'à ce que l'on y arrive. - Nelson Mandela" tip_talk_is_cheap: "Discuter est facile. Montrez-moi le code. - Linus Torvalds" - tip_first_language: "la chose la plus désastreuse que vous ne pourrez jamais apprendre est votre premier langage de programmation. - Alan Kay" - tip_hardware_problem: "Q: Combien de programmeurs faut-il pour changer une ampoule? R: Aucun, c'est un problème de matériel." - tip_hofstadters_law: "Loi de Hofstadter: Il faut toujours plus de temps que prévu, même si vous prenez en compte la loi de Hofstadter." + tip_first_language: "La chose la plus désastreuse que vous ne pourrez jamais apprendre est votre premier langage de programmation. - Alan Kay" + tip_hardware_problem: "Q: Combien de programmeurs faut-il pour changer une ampoule ? R: Aucun, c'est un problème de matériel." + tip_hofstadters_law: "Loi de Hofstadter : Il faut toujours plus de temps que prévu, même si vous prenez en compte la loi de Hofstadter." tip_premature_optimization: "L'optimisation prématurée est la racine de tous les maux. - Donald Knuth" tip_brute_force: "En cas de doute, utiliser la force brute. - Ken Thompson" - customize_wizard: "Personnaliser le magicien" + tip_extrapolation: "Il y a seulement deux types de personnes : celles qui peuvent extrapoler à partir de données incomplètes..." + tip_superpower: "Le développement est la chose la plus proche d'un super pouvoir." + tip_control_destiny: "Dans le vrai open source, vous avez le controle sur votre propre destinée. - Linus Torvalds" + tip_no_code: "Aucun code n'est plus rapide qu'aucun code." + tip_code_never_lies: "Le code ne ment jamais, les commentaires... parfois — Ron Jeffries" + tip_reusable_software: "Avant qu'un logiciel soit réutilisable, il doit être utilisable" + tip_optimization_operator: "Tout les langages ont un opérateur d'optimisation. Dans la plupart des langages, cet opérateur est ‘//’" + tip_lines_of_code: "Mesurer l'avancé d'un programme par le nombre de lignes de code, c'est comme mesurer l'avancé d'un avion par son poid. - Bill Gates" + tip_source_code: "Je voudrais changer le monde, mais ils ne veulent pas me donner le code source." + tip_javascript_java: "le Java est au Javascript ce que le tapis est à la tapisserie" + tip_move_forward: "Quoi que vous fassiez, continuez d'avancer. - Martin Luther King Jr." + tip_google: "*Vous avez un problème que vous n'arrivez pas à résoudre ? Googlez le !" + tip_adding_evil: "Ajout d'une pincée de méchanceté" + tip_hate_computers: "C'est ça le problème avec les gens qui pensent qu'ils détestent les ordinateur. Ce qu'ils détestent vraiment, c'est des piètres programmeurs. - Larry Niven" + tip_open_source_contribute: "Vous pouvez aider CodeCombat à s'améliorer !" + tip_recurse: "Itérer c'est humain, mais faire des récursion, c'est divin. - L. Peter Deutsch" + tip_free_your_mind: "Vous devez laisser aller , Neo . La peur , le doute et l'incrédulité . Libère ton esprit. - Morpheus" + tip_strong_opponents: "Même les ennemis les plus puissant ont une faiblesse. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventaire" save_load_tab: "Sauvegarder/Charger" -# options_tab: "Options" -# guide_tab: "Guide" + options_tab: "Options" + guide_tab: "Guide" + guide_video_tutorial: "Tutoriel vidéo" + guide_tips: "Astuces" multiplayer_tab: "Multijoueur" auth_tab: "S'inscrire" - inventory_caption: "Équipez votre héro" - choose_hero_caption: "Choisissez votre héro, langage" + inventory_caption: "Équipez votre héros" + choose_hero_caption: "Choisissez votre héros, langage" save_load_caption: "... et voir l'historique" options_caption: "Configurer les réglages" - guide_caption: "Docs and conseils" - multiplayer_caption: "Jouer avec des amis!" + guide_caption: "Docs et conseils" + multiplayer_caption: "Jouer avec des amis !" auth_caption: "Sauvegarder votre progression." + leaderboard: + leaderboard: "Classement" + view_other_solutions: "Voir les autres solutions" # {change} + scores: "Scores" + top_players: "Classement des meilleurs joueurs" + day: "Aujourd'hui" + week: "Cette semaine" + all: "Tous les temps" + time: "Temps" + damage_taken: "Dégât subis" + damage_dealt: "Dégât infligés" + difficulty: "Difficulté" + gold_collected: "Or collecté" + inventory: choose_inventory: "Équiper des Objets" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + equipped_item: "Equipé" + required_purchase_title: "Requis" + available_item: "Disponible" + restricted_title: "Limité" + should_equip: "(double-clic pour s'équiper)" + equipped: "(équipé)" + locked: "(verrouillé)" + restricted: "(verrouillé à ce niveau)" + equip: "Equiper" + unequip: "Retirer" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + buy_gems: + few_gems: "Quelques gemmes" + pile_gems: "Pile de gemmes" + chest_gems: "Coffre de gemmes" + purchasing: "Achat..." + declined: "Votre carte a été refusée" + retrying: "Erreur serveur, nouvelle tentative." + prompt_title: "Pas assez de gemmes" + prompt_body: "En voulez-vous plus ?" + prompt_button: "Entrer dans la boutique" + recovered: "Gemmes précédemment achetées récupérées. Merci de rafraîchir la page." + price: "x3500 / mo" + + subscribe: + comparison_blurb: "Aiguisez vos compétences avec un abonnement CodeCombat !" + feature1: "Plus de 60 niveaux au travers de 4 mondes" # {change} + feature2: "7 puissants <strong>nouveaux héros</strong> avec des compétences uniques !" # {change} + feature3: "Plus de 30 niveaux bonus" # {change} + feature4: "<strong>3500 gemmes bonus</strong> tous les mois !" + feature5: "Tutoriels vidéo" + feature6: "Assitance par e-mail dédiée" + feature7: "<strong>Clans</strong> privés" + free: "Gratuit" + month: "mois" + subscribe_title: "Abonnement" + unsubscribe: "Désinscription" + confirm_unsubscribe: "Confirmer la désinscription" + never_mind: "Ça ne fait rien, je t'aime toujours" + thank_you_months_prefix: "Merci de nous avoir supporté pendant" + thank_you_months_suffix: "mois." + thank_you: "Merci de supporter CodeCombat." + sorry_to_see_you_go: "Désolé de te voir partir ! Fais-nous savoir ce que nous aurions pu faire mieux" + unsubscribe_feedback_placeholder: "Nom de zeus, qu'avons nous fait ?" + parent_button: "Demande à tes parents" + parent_email_description: "Nous leur enverrons un e-mail pour qu'ils t'achètent un abonnement CodeCombat." + parent_email_input_invalid: "Adresse e-mail non valide" + parent_email_input_label: "Adresse e-mail des parents" + parent_email_input_placeholder: "Entrer l'adresse e-mail des parents" + parent_email_send: "Envoyer l'e-mail" + parent_email_sent: "E-mail envoyé !" + parent_email_title: "Quelle est l'adresse e-mail de tes parents ?" + parents: "Pour les parents" + parents_title: "Votre enfant va apprendre à programmer." # {change} + parents_blurb1: "Avec CodeCombat, votre enfant apprend en écrivant de vrais programmes. Il commence en apprenant des instructions simples, puis progresse sur des thèmes plus complexes." + parents_blurb1a: "La programmation informatique est une compétence essentielle que votre fils va indubitablement utiliser comme un adulte. En 2020, les compétences basiques en informatique seront obligatoires pour 77% des travails, et les ingénieurs logiciels seront fortement demandés partout dans le monde. Saviez-vous que l'informatique est la compétence universitaire la mieux payée ?" + parents_blurb2: "Pour $9.99 USD/mois, il obtient de nouveaux défis chaque semaine et le support par e-mail de programmeurs professionnels." # {change} + parents_blurb3: "Pas de risque : garantie 100% remboursé, désinscription facile en 1 clic." + payment_methods: "Moyens de paiement" + payment_methods_title: "Moyens de paiement acceptés" + payment_methods_blurb1: "Nous acceptons, pour le moment, les cartes de crédit et les paiment par Alipay." + payment_methods_blurb2: "Si vous avez besoins d'un autre moyen de paiement, merci de nous contacter" + stripe_description: "Inscription mensuelle" + subscription_required_to_play: "Vous avez besoin d'un abonnement pour jouer à ce niveau." + unlock_help_videos: "Abonnez vous pour débloquer tous les tutoriels vidéo." + personal_sub: "Abonnement individuel" # Accounts Subscription View below + loading_info: "Chargement des informations sur votre abonnement..." + managed_by: "Gérer par" + will_be_cancelled: "Ceci va être annuler" + currently_free: "Vous avez un abonnement gratuit en ce moment" + currently_free_until: "Vous avez un abonnement gratuit jusqu'à" + was_free_until: "Vous aviez un abonnement gratuit jusqu'à" + managed_subs: "Gestion des abonnements" + managed_subs_desc: "Ajout d'abonnements pour les autres joueurs (étudiants,enfants,etc.) for other players." + managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." + group_discounts: "Rabais de groupes" + group_discounts_1: "Nous offrons des rabais de groupe pour les gros abonnements" + group_discounts_1st: "Premier abonnement" + group_discounts_full: "Plein prix" + group_discounts_2nd: "Abonnements 2-11" + group_discounts_20: "Rabais de 20%" + group_discounts_12th: "Abonnements 12+" + group_discounts_40: "Rabais de 40%" + subscribing: "S'inscrit..." + recipient_emails_placeholder: "Entrez votre courriel pour vous abonner, un par ligne." + subscribe_users: "Seulement pour les usagers aboonés" + users_subscribed: "Usagers abonnés:" + no_users_subscribed: "Aucun usager abonnés, veuillez vérifier vos courriels." + current_recipients: "Recipients courant" + unsubscribing: "Desincription en cours..." + subscribe_prepaid: "Cliquer S'abonner pour utiliser du code prépayé" + using_prepaid: "Utiliser le code prépayé pour un abonnement mensuel" choose_hero: - choose_hero: "Choisissez votre Héro" + choose_hero: "Choisissez votre héros" programming_language: "Langage de programmation" - programming_language_description: "Quel langage de programmation voullez-vous utiliser?" -# default: "Default" -# experimental: "Experimental" + programming_language_description: "Quel langage de programmation voulez-vous utiliser ?" + default: "Défaut" + experimental: "Expérimental" python_blurb: "Simple mais puissant, idéal pour les débutants et les experts." javascript_blurb: "Le langage du web. (Pas le même que Java.)" coffeescript_blurb: "Une syntaxe Javascript plus agréable." clojure_blurb: "Un Lisp moderne." lua_blurb: "Langage de script de jeu." - io_blurb: "Simple mais obscure." -# status: "Status" + io_blurb: "Simple mais obscur." + status: "Statut" + hero_type: "Type" weapons: "Arme" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" - attack: "Dégât" # Can also translate as "Attack" + weapons_warrior: "Epées - Courte portée, pas de magie" + weapons_ranger: "Arbalètes, pistolets - Longue portée, pas de magie" + weapons_wizard: "Baguettes, bâtons - Longue portée, Magie" + attack: "Dégâts" # Can also translate as "Attack" health: "Santé" speed: "Vitesse" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" + regeneration: "Régénération" + range: "Portée" # As in "attack or visual range" + blocks: "Absorbe" # As in "this shield blocks this much damage" + backstab: "Poignardé" # As in "this dagger does this much backstab damage" skills: "Compétences" + attack_1: "Inflige" + attack_2: "Classé de" + attack_3: "Dommage causé par l'arme" + health_1: "Gains" + health_2: "Provenance de la liste" + health_3: "Endurance de l'armure" + speed_1: "Se mouvoit à" + speed_2: "mètres par seconde" + available_for_purchase: "Disponible à l'achat" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Niveau à débloquer :" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Seulement certains héros peuvent jouer ce niveau." -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + skill_docs: + writable: "éditable" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "lecture seulement" + action_name: "nom" + action_cooldown: "Durée" + action_specific_cooldown: "Rechargement" + action_damage: "Dégât" + action_range: "Portée" + action_radius: "Rayon" + action_duration: "Durée" + example: "Exemple" + ex: "ex" # Abbreviation of "example" + current_value: "Valeur actuelle" + default_value: "Valeur par défaut" + parameters: "Paramètres" + returns: "Retour" + granted_by: "Accordé par" save_load: granularity_saved_games: "Sauvegardé" @@ -375,17 +527,15 @@ module.exports = nativeDescription: "français", englishDescription: "French", t options: general_options: "Options Générales" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" + volume_label: "Volume" music_label: "Musique" music_description: "Arrêter/Reprendre la musique de fond." - autorun_label: "Auto-exécution" - autorun_description: "Controler l'exécution automatique du code." editor_config: "Config de l'éditeur" editor_config_title: "Configuration de l'éditeur" editor_config_level_language_label: "Langage pour le niveau" - editor_config_level_language_description: "Définir le langage de programmation pour le niveaux en particulier." - editor_config_default_language_label: "Langage de Programmation par défaut" - editor_config_default_language_description: "Choississez le langage de programmation que vous souhaitez dons les nouveaux niveaux" + editor_config_level_language_description: "Définir le langage de programmation pour le niveau en particulier." + editor_config_default_language_label: "Langage de programmation par défaut" + editor_config_default_language_description: "Choississez le langage de programmation que vous souhaitez dans les nouveaux niveaux" editor_config_keybindings_label: "Raccourcis clavier" editor_config_keybindings_default: "Par défault (Ace)" editor_config_keybindings_description: "Ajouter de nouveaux raccourcis connus depuis l'éditeur commun." @@ -399,50 +549,144 @@ module.exports = nativeDescription: "français", englishDescription: "French", t editor_config_behaviors_description: "Ferme automatiquement les accolades, parenthèses, et chaînes de caractères." about: - why_codecombat: "Pourquoi CodeCombat?" - why_paragraph_1: "Besoin d'apprendre à développer? Vous n'avez pas besoin de cours. Vous avez besoin d'écrire beaucoup de code et de vous amuser en le faisant." + why_codecombat: "Pourquoi CodeCombat ?" + why_paragraph_1: "Besoin d'apprendre à développer ? Vous n'avez pas besoin de cours. Vous avez besoin d'écrire beaucoup de code et de vous amuser en le faisant." why_paragraph_2_prefix: "C'est ce dont il s'agit en programmation. Ça doit être amusant. Pas amusant comme" why_paragraph_2_italic: "Génial un badge" why_paragraph_2_center: "Mais amusant comme" - why_paragraph_2_italic_caps: "NAN MAMAN JE DOIS FINIR MON NIVEAU!" + why_paragraph_2_italic_caps: "NAN MAMAN JE DOIS FINIR MON NIVEAU !" why_paragraph_2_suffix: "C'est pourquoi CodeCombat est un jeu multijoueur, pas un cours avec une leçon jouée. Nous n'arrêterons pas avant que vous ne puissiez plus arrêter — mais cette fois, c'est une bonne chose." why_paragraph_3: "Si vous devez devenir accro à un jeu, accrochez-vous à celui-ci et devenez un des mages de l'âge de la technologie." press_title: "Bloggueurs/Presse" - press_paragraph_1_prefix: "Vous voulez écrire sur nous? Sentez-vous libre de télécharger et d'utiliser toutes les ressources incluses dans notre" + press_paragraph_1_prefix: "Vous voulez écrire sur nous ? Sentez-vous libre de télécharger et d'utiliser toutes les ressources incluses dans notre" press_paragraph_1_link: "paquet de presse" - press_paragraph_1_suffix: ". Tous les logos et images peuvent être utilisées sans nous contacter directement." + press_paragraph_1_suffix: ". Tous les logos et images peuvent être utilisés sans nous contacter directement." team: "Équipe" - george_title: "PDG" + george_title: "PDG" # {change} george_blurb: "Homme d'affaires" - scott_title: "Programmeur" + scott_title: "Programmeur" # {change} scott_blurb: "Raisonable" - nick_title: "Programmeur" + nick_title: "Programmeur" # {change} nick_blurb: "Gourou de Motivation" michael_title: "Programmeur" michael_blurb: "Sys Admin" - matt_title: "Programmeur" + matt_title: "Programmeur" # {change} matt_blurb: "Bicycliste" + cat_title: "Chef Artisan" + cat_blurb: "Seigneur de l'air" + josh_title: "Designer de jeu" + josh_blurb: "Le plancher est de la lave" + jose_title: "Musique" + jose_blurb: "Décollage" + retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat pour les enseignants" + intro_1: "CodeCombat est un jeu en ligne qui enseigne la programmation. Les élèves écrivent du code dans de vrais langages de programmation." + intro_2: "Aucune expérience requise !" + free_title: "Combien cela coûte-t-il ?" + cost_china: "CodeCombat en Chine est gratuit pour les cinq premiers niveaux,après le jeu coûte 9.99$ US par mois pour avoir un accès aux autres 140+ niveaux sur les serveurs exlcusifs chinois" + free_1: "La version de base de CodeCombat est gratuite ! Il y a 70+ niveaux gratuits qui couvrent chaque concepts." # {change} + free_2: "Un abonnement mensuel fournit l'accès à des vidéos de tutoriels ainsi qu'à des niveaux d'entraînement supplémentaires." + teacher_subs_title: "Les enseignants reçoivent un abonnement gratuit !" + teacher_subs_1: "Merci de nous contacter" # {change} + teacher_subs_2: "pour obtenir un abonnement mensuel gratuit." # {change} +# teacher_subs_3: "to set up your subscription." + sub_includes_title: "Qu'y a t-il d'inclus dans cet abonnement ?" + sub_includes_1: "En plus des 70+ niveaux de base, les élèves avec un abonnement mensuel ont accès à ces fonctionnalités supplémentaires :" # {change} + sub_includes_2: "40+ niveaux d'entrainement" # {change} + sub_includes_3: "Des tutoriels vidéo" + sub_includes_4: "Support email premium" + sub_includes_5: "7 nouveaux héros avec des capacités uniques à maitriser" # {change} + sub_includes_6: "3500 gemmes bonus chaque mois" + sub_includes_7: "Clans Privées" + monitor_progress_title: "Comment puis-je faire pour surveiller les progrès des étudiants?" + monitor_progress_1: "Le progès des étudiants peut être surveiller en créant un" + monitor_progress_2: "pour votre classe" + monitor_progress_3: "Pour ajouter un étudiant, envoyer leur le lien contenant une invitation pour votre Clan" + monitor_progress_4: "page." + monitor_progress_5: "Après leur adhésion, vous verrez un résumé des progrès de l'élève sur la page de votre clan." + private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." + private_clans_2: "Pour créer un Clan privé, veuillez vous référer à la boîte à cocher 'Faire un clan privé' pendant la création du" + private_clans_3: "." + who_for_title: "A qui CodeCombat est t-il destiné ?" + who_for_1: "Nous recommandons CodeCombat pour les élèves âgés de 9 ans ou plus. Aucune expérience préalable de programmation n'est requise." + who_for_2: "Nous avons conçu CodeCombat pour plaire à la fois aux garçons et aux filles." + material_title: "Quelle quantité de contenu y a t-il ?" + material_china: "Il y a en moyenne une trentaine d'heures de temps de jeu distribué sur plus de 140 abonnés jusqu'à maintenant avec des nouveaux niveaux qui s'ajoutent à chaque semaine." # {change} + material_1: "Environ 8 heures de contenu gratuit et 14 heures de contenu supplémentaire reservé aux abonnés, avec 5 nouveaux niveaux chaque semaines." # {change} + concepts_title: "Quels concepts sont couverts ?" + how_much_title: "Combien coûte un abonnement mensuel ?" + how_much_1: "Un" + how_much_2: "abonnement mensuel" + how_much_3: "coûte $9.99 et peut être annulé à n'importe quel moment." + how_much_4: "De plus, nous fournissons des remises pour les grands groupes :" + how_much_5: "Nous acceptons les achats ponctuels à rabais ainsi que les abonnements pour les groupes, tel que pour une classe ou un groupe. Veuillez nous contacter." + how_much_6: "pour plus de détails" + more_info_title: "À quel endroit je peux trouver plus de détails ?" + more_info_1: "Notre" + more_info_2: "forum d'enseignants" + more_info_3: "C'est un bon endroit pour faire des relations entre vos camarades éducateurs utilisant CodeCombat" + sys_requirements_title: "Configuration requise" + sys_requirements_1: "Un navigateur moderne. La dernière version de Chrome, Firefox, ou de Safari. Internet Explorer 9 ou plus récent." + sys_requirements_2: "CodeCombat n'est pas encore supporté sur iPad." + + teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." + email: "Adresse email" + school: "Nom de l'école" + location: "Nom de la ville" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Enregistrer une nouvelle version" new_major_version: "Nouvelle version majeure" + submitting_patch: "Soumission du correctif..." cla_prefix: "Pour enregistrer vos modifications vous devez d'abord accepter notre" cla_url: "Copyright" cla_suffix: "." cla_agree: "J'accepte" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contacter CodeCombat" - welcome: "Ravi d'avoir de vos nouvelles! Utilisez ce formulaire pour nous envoyer un mail." - contribute_prefix: "Si vous voulez contribuer, consultez notre " - contribute_page: "page de contributions" - contribute_suffix: "!" - forum_prefix: "Pour tout sujet d'ordre publique, merci d'utiliser " + welcome: "Ravi d'avoir de vos nouvelles ! Utilisez ce formulaire pour nous envoyer un e-mail." + forum_prefix: "Pour tout sujet d'ordre public, merci d'utiliser " forum_page: "notre forum" forum_suffix: " À la place." + faq_prefix: "Il y a aussi une" + faq: "FAQ" + subscribe_prefix: "Si vous avez besoin d'aide pour la résolution d'un niveau" + subscribe: "achetez un abonnement CodeCombat" + subscribe_suffix: "et nous seront ravis de vous aider avec votre code." + subscriber_support: "Etant donné que vous êtes abonné à CodeCombat, votre message sera traité en priorité par notre support." + screenshot_included: "Capture d'écran ajoutée." + where_reply: "Où devons-nous répondre ?" send: "Envoyer un commentaire" contact_candidate: "Contacter le candidat" # Deprecated - recruitment_reminder: "Utilisez ce formulaire pour entrer en contact avec le candidat qui vous interesse. Souvenez-vous que CodeCombat facture 15% de la première année de salaire. Ces frais sont dues à l'embauche de l'employé, ils sont remboursable pendant 90 jours si l'employé ne reste pas employé. Les employés à temps partiel, à distance ou contractuel sont gratuits en tant que stagiaires." # Deprecated + recruitment_reminder: "Utilisez ce formulaire pour entrer en contact avec le candidat qui vous intéresse. Souvenez-vous que CodeCombat facture 15% de la première année de salaire. Ces frais sont dus à l'embauche de l'employé, ils sont remboursables pendant 90 jours si l'employé ne reste pas employé. Les employés à temps partiel, à distance ou contractuel sont gratuits en tant que stagiaires." # Deprecated account_settings: title: "Préférences du compte" @@ -450,25 +694,32 @@ module.exports = nativeDescription: "français", englishDescription: "French", t autosave: "Enregistrer automatiquement les modifications" me_tab: "Moi" picture_tab: "Photos" - upload_picture: "Héberger une image" + delete_account_tab: "Supprimer votre compte" + wrong_email: "Mauvaise adresse e-mail" + wrong_password: "Mauvais mot de passe" + upload_picture: "Télécharger une image" + delete_this_account: "Supprimer votre compte définitivement" + god_mode: "Puissance Divine" password_tab: "Mot de passe" - emails_tab: "Emails" + emails_tab: "E-mails" admin: "Admin" new_password: "Nouveau mot de passe" new_password_verify: "Vérifier" + type_in_email: "Entrez votre adresse e-mail pour confirmer la supression de votre compte" # {change} +# type_in_password: "Also, type in your password." email_subscriptions: "Abonnements" - email_subscriptions_none: "Aucun email d'abonnement." + email_subscriptions_none: "Aucun e-mail d'abonnement." email_announcements: "Annonces" - email_announcements_description: "Recevoir des mails sur les dernières actualités et sur le développement de CodeCombat." + email_announcements_description: "Recevoir des e-mails sur les dernières actualités et sur le développement de CodeCombat." email_notifications: "Notifications" - email_notifications_summary: "Commandes pour personaliser les notifications automatiques d'email liées à votre activité sur CodeCombat." - email_any_notes: "Toutes Notifications" + email_notifications_summary: "Commandes pour personaliser les notifications automatiques d'e-mail liées à votre activité sur CodeCombat." + email_any_notes: "Toutes les notifications" email_any_notes_description: "Désactivez pour ne plus recevoir de notifications par e-mail." email_news: "Actualités" email_recruit_notes: "Offres d'emploi" email_recruit_notes_description: "Si vous jouez vraiment bien, nous pouvons vous contacter pour vous proposer un (meilleur) emploi." - contributor_emails: "Emails des contributeurs" - contribute_prefix: "Nous recherchons des personnes pour se joindre à notre groupe! Consultez la " + contributor_emails: "E-mails des contributeurs" + contribute_prefix: "Nous recherchons des personnes pour se joindre à notre groupe ! Consultez la " contribute_page: "page de contributions" contribute_suffix: " pour en savoir plus." email_toggle: "Tout basculer" @@ -477,20 +728,19 @@ module.exports = nativeDescription: "français", englishDescription: "French", t password_mismatch: "Le mot de passe ne correspond pas." password_repeat: "Veuillez s'il vous plaît répéter votre mot de passe" job_profile: "Profil d'emploi" # Rest of this section (the job profile stuff and wizard stuff) is deprecated - job_profile_approved: "Votre profil d'emploi a été approuvé par CodeCombat. Les employeurs seront en mesure de voir votre profil jusqu'à ce que vous le marquez inactif ou qu'il n'a pas été changé pendant quatre semaines." - job_profile_explanation: "Salut! Remplissez-le et nous prendrons contact pour vous trouver un emploi de développeur de logiciels." + job_profile_approved: "Votre profil d'emploi a été approuvé par CodeCombat. Les employeurs seront en mesure de voir votre profil jusqu'à ce que vous le marquiez inactif ou qu'il n'aie pas été changé pendant quatre semaines." + job_profile_explanation: "Salut ! Remplissez-le et nous prendrons contact pour vous trouver un emploi de développeur de logiciels." sample_profile: "Voir un exemple de profil" view_profile: "Voir votre profil" - wizard_tab: "Magicien" - wizard_color: "Couleur des vêtements du Magicien" keyboard_shortcuts: keyboard_shortcuts: "Raccourcis Clavier" space: "Espace" enter: "Entrer" + press_enter: "Appuyez sur entrée" escape: "Echap" -# shift: "Shift" -# run_code: "Run current code." + shift: "Shift" + run_code: "Exécuter le code actuel." run_real_time: "Exécuter en temps réel." continue_script: "Continuer le script passé courant." # skip_scripts: "Skip past all skippable scripts." @@ -500,42 +750,91 @@ module.exports = nativeDescription: "français", englishDescription: "French", t # scrub_execution: "Scrub through current spell execution." toggle_debug: "Afficher la console de déboggage" toggle_grid: "Afficher une grille" -# toggle_pathfinding: "Toggle pathfinding overlay." + toggle_pathfinding: "Toggle pathfinding overlay." beautify: "Embellissez votre code en normalisant sa mise en forme." maximize_editor: "Maximiser/minimiser l'éditeur de code." - move_wizard: "Déplacer votre Magicien à travers le niveau." community: main_title: "Communauté CodeCombat" - introduction: "Découvrez les façons dont vous pouvez vous impliquer ci-dessous et de décider ce qui semble le plus amusant. Nous sommes impatients de travailler avec vous!" + introduction: "Découvrez les façons dont vous pouvez vous impliquer ci-dessous et décidez ce qui semble le plus amusant. Nous sommes impatients de travailler avec vous !" level_editor_prefix: "Utiliser le CodeCombat" - level_editor_suffix: "pour créer et modifier des niveaux. Les utilisateurs ont créé des niveaux pour leurs classes, amis, hackathons, étudiants et leurs frères et sœurs. Si créer un nouveau niveau est intimidant, vous pouvez commencer par forker un des nôtres!" - thang_editor_prefix: "Nous appelons les unités dans le jeu 'thangs'. Utilisez le" + level_editor_suffix: "pour créer et modifier des niveaux. Les utilisateurs ont créé des niveaux pour leurs classes, amis, hackathons, étudiants et leurs frères et sœurs. Si créer un nouveau niveau est intimidant, vous pouvez commencer par forker un des nôtres !" + thang_editor_prefix: "Nous appelons les unités dans le jeu 'thangs'. Utilisez-le" thang_editor_suffix: "pour modifier l'illustration de la source de CodeCombat. Permettre aux unités de lancer des projectiles, de modifier la direction de l'animation, de changer les points de vie d'une unité, ou de télécharger vos propres sprites vectorielles." - article_editor_prefix: "Vous voyez une erreur dans certaines de nos docs? Vous voulez faire des instructions pour vos propres créations? Découvrez le" - article_editor_suffix: "et aider les joueurs CodeCombat à tirer le meilleur parti de leur temps de jeu." - find_us: "Trouvez nous sur ces sites" + article_editor_prefix: "Vous voyez une erreur dans certaines de nos docs ? Vous voulez faire des instructions pour vos propres créations ? Découvrez-le" + article_editor_suffix: "et aidez les joueurs CodeCombat à tirer le meilleur parti de leur temps de jeu." + find_us: "Trouvez-nous sur ces sites" social_blog: "Lire le blog CodeCombat sur Sett" social_discource: "Participez à la discussion sur notre forum Discourse" social_facebook: "Aimer CodeCombat sur Facebook" social_twitter: "Suivre CodeCombat sur Twitter" social_gplus: "Rejoindre CodeCombat sur Google+" social_hipchat: "Chattez avec nous dans la salle HipChat publique CodeCombat" - contribute_to_the_project: "Contribuer au project" + contribute_to_the_project: "Contribuer au projet" + + clans: + clan: "Clan" + clans: "Clans" + new_name: "Nouveau nom de clan" + new_description: "Description du nouveau clan" + make_private: "Rendre le clan privé" + subs_only: "Abonnés seulement" + create_clan: "Créer un nouveau clan" + private_preview: "Aperçu" + public_clans: "Clans publiques" + my_clans: "Mes clans" + clan_name: "Nom du clan" + name: "Nom" + chieftain: "Chef" + type: "Type" + edit_clan_name: "Modifier le nom du clan" + edit_clan_description: "Modifier la description du clan" + edit_name: "Modifier nom" + edit_description: "modifier description" + private: "(privé)" + summary: "Sommaire" + average_level: "Moyenne de niveau" + average_achievements: "Moyenne de réalisations" + delete_clan: "Délétion de clan" + leave_clan: "Partir du clan" + join_clan: "Rejoindre le clan" + invite_1: "Inviter:" + invite_2: "*Inviter des joueurs au Caln en leur envoyant le lien suivant." + members: "Membres" + progress: "Progression" + not_started_1: "Non commencé" + started_1: "démarré" + complete_1: "compléter" + exp_levels: "Étendre les niveaux" + rem_hero: "Retirer l'héros" + status: "Status" + complete_2: "Compléter" + started_2: "Démarré" + not_started_2: "Non démarré" + view_solution: "Cliquer pour voir la solution" + latest_achievement: "Réalisation Récentes" + playtime: "Temps de jeu" + last_played: "Dernière période de jeu" classes: archmage_title: "Archimage" archmage_title_description: "(Développeur)" + archmage_summary: "Si vous êtes développeur, intéréssé par le développement de jeux éducatifs, devenez un Archimage et aidez-nous à construire CodeCombat !" artisan_title: "Artisan" artisan_title_description: "(Créateur de niveau)" + artisan_summary: "Créez et partagez des niveaux pour y jouer avec vos amis. Devenz un Artisan pour apprendre l'art d'enseigner la programmation." adventurer_title: "Aventurier" adventurer_title_description: "(Testeur de niveau)" + adventurer_summary: "Jouez aux derniers niveaux (même ceux reservés aux abonnés) une semaine en avance gratuitement, et aidez-nous à déceler les bugs avant la sortie publique." scribe_title: "Scribe" scribe_title_description: "(Rédacteur d'articles)" + scribe_summary: "Un bon code nécéssite une bonne documentation. Ecrivez, corrigez et améliorez la documentation lue par des millions de joueurs autour du monde." diplomat_title: "Diplomate" diplomat_title_description: "(Traducteur)" + diplomat_summary: "CodeCombat est traduit dans plus de 45 langues par nos Diplomates. Aidez-nous, et contribuez à ces traductions." ambassador_title: "Ambassadeur" ambassador_title_description: "(Aide)" + ambassador_summary: "Domptez les membres du forum, et guidez ceux qui ont besoin d'aide. Nos ambassadeurs représentent CodeCombat face au monde." editor: main_title: "Éditeurs CodeCombat" @@ -543,31 +842,46 @@ module.exports = nativeDescription: "français", englishDescription: "French", t thang_title: "Éditeur Thang" level_title: "Éditeur de niveau" achievement_title: "Éditeur de succès" + poll_title: "Éditeur de sondage" back: "Retour" revert: "Annuler" revert_models: "Annuler les modèles" pick_a_terrain: "Choisir un terrain" - small: "Petit" + dungeon: "Donjon" + indoor: "Intérieur" + desert: "Desert" grassy: "Herbeux" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Petit" + large: "Large" fork_title: "Fork une nouvelle version" fork_creating: "Créer un Fork..." generate_terrain: "Générer le terrain" more: "Plus" wiki: "Wiki" - live_chat: "Chat en live" - level_some_options: "Quelques options?" + live_chat: "Chat en direct" + thang_main: "Principal" + thang_spritesheets: "Feuilles des sprites" + thang_colors: "Couleurs" + level_some_options: "Quelques options ?" level_tab_thangs: "Thangs" level_tab_scripts: "Scripts" level_tab_settings: "Paramètres" level_tab_components: "Composants" level_tab_systems: "Systèmes" -# level_tab_docs: "Documentation" + level_tab_docs: "Documentation" level_tab_thangs_title: "Thangs actuels" level_tab_thangs_all: "Tout" level_tab_thangs_conditions: "Conditions de départ" - level_tab_thangs_add: "ajouter des Thangs" + level_tab_thangs_add: "Ajouter des Thangs" +# level_tab_thangs_search: "Search thangs" + add_components: "Ajouter des composants" + component_configs: "Configuration des composants" + config_thang: "Double-cliquez pour configurer un Thang" delete: "Supprimer" duplicate: "Dupliquer" + stop_duplicate: "Arreter la duplication" rotate: "Pivoter" level_settings_title: "Paramètres" level_component_tab_title: "Composants actuels" @@ -585,108 +899,98 @@ module.exports = nativeDescription: "français", englishDescription: "French", t new_component_title: "Créer un nouveau composant" new_component_field_system: "Système" new_article_title: "Créer un nouvel article" - new_thang_title: "Créer un nouveau Type Thang" + new_thang_title: "Créer un nouveau type de Thang" new_level_title: "Créer un nouveau niveau" new_article_title_login: "Identifiez-vous pour créer un nouvel article" new_thang_title_login: "Identifiez-vous pour créer un nouveau type de Thang" new_level_title_login: "Identifiez-vous pour créer un nouveau niveau" new_achievement_title: "Créer un nouveau succès" new_achievement_title_login: "Connectez vous pour créer un nouveau succès" + new_poll_title: "Créer un nouveau sondage" + new_poll_title_login: "Connectez vous pour créer un nouveau sondage" article_search_title: "Rechercher dans les articles" - thang_search_title: "Rechercher dans les types Thang" + thang_search_title: "Rechercher dans les types de Thang" level_search_title: "Rechercher dans les niveaux" achievement_search_title: "Rechercher des succès" + poll_search_title: "Recherche de sondages" read_only_warning2: "Note: vous ne pouvez sauvegarder aucune édition, car vous n'êtes pas identifié." - no_achievements: "Aucuns succès ont encore été ajoutés à ce niveau." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" + no_achievements: "Aucun succès n'a encore été ajouté à ce niveau." + achievement_query_misc: "Réalisations clés en provenance de miscellanea" + achievement_query_goals: "Succés important hors des objectifs de niveau" level_completion: "Niveau d'achèvement" -# pop_i18n: "Populate I18N" + pop_i18n: "Renseigner I18N" + tasks: "Tâches" + clear_storage: "Vider vos changements locaux" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Prévisualiser" edit_article_title: "Éditer l'article" + polls: + priority: "Priorité" + contribute: page_title: "Contribution" - character_classes_title: "Classes du personnage" - introduction_desc_intro: "Nous avons beaucoup d'espoir pour CodeCombat." - introduction_desc_pref: "Nous voulons être l'endroit où les développeurs de tous horizons viennent pour apprendre et jouer ensemble, présenter les autres au monde du développement, et refléter le meilleur de la communauté. Nous ne pouvons et ne voulons pas faire ça seuls ; ce qui rend super les projets comme GitHub, Stack Overflow et Linux, est que les gens qui l'utilisent le construisent. Dans ce but, " - introduction_desc_github_url: "CodeCombat est totalement open source" - introduction_desc_suf: ", et nous avons pour objectif de fournir autant de manières possibles pour que vous participiez et fassiez de ce projet autant le votre que le notre." - introduction_desc_ending: "Nous espérons que vous allez joindre notre aventure!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy et Matt" - alert_account_message_intro: "Et tiens!" + intro_blurb: "CodeCombat est 100% open source ! Des centaines de joueurs dévoués nous ont aidé à construire le jeu tel qu'il est aujourd'hui. Rejoignez-nous et écrivez le prochain chapitre dans la quête de CodeCombat d'enseigner au monde à coder !" + alert_account_message_intro: "Et tiens !" alert_account_message: "Pour souscrire aux e-mails, vous devez être connecté" - archmage_summary: "Intéressé à travailler sur les graphismes du jeu, la conception de l'interface utilisateur, la base de données et l'organisation de serveur, réseau multijoueur, la physique, le son, ou la performance du moteur de jeu? Vous voulez aider à construire un jeu pour aider les autres à apprendre à ce à quoi vous êtes bon? Nous avons beaucoup à faire et si vous êtes un programmeur expérimenté et souhaitez développer pour CodeCombat, cette classe est pour vous. Nous aimerions votre aide pour bâtir le meilleur jeu de programmation ayant jamais existé." - archmage_introduction: "L'une des meilleures parties de la création d'un jeu est qu'il regroupe tant de choses différentes. Graphismes, sons, réseau en temps réel, réseaux sociaux, et bien sur bien d'autres aspects de la programmation, de la gestion bas niveau de base de données, et de l'administration de serveur à l'élaboration d'interfaces utilisateur. Il y a tant à faire, et si vous êtes un programmeur expérimenté avec une aspiration à vraiment plonger dans le fond de CodeCombat, cette classe est faite pour vous. Nous aimerions avoir votre aide pour le meilleur jeu de développement de tous les temps." + archmage_introduction: "L'une des meilleures parties de la création d'un jeu est qu'il regroupe tant de choses différentes. Graphismes, sons, réseau en temps réel, réseaux sociaux, et bien sûr bien d'autres aspects de la programmation, de la gestion bas niveau de base de données, et de l'administration de serveur à l'élaboration d'interfaces utilisateur. Il y a tant à faire, et si vous êtes un programmeur expérimenté avec une aspiration à vraiment plonger dans le fond de CodeCombat, cette classe est faite pour vous. Nous aimerions avoir votre aide pour le meilleur jeu de développement de tous les temps." class_attributes: "Attributs de classe" archmage_attribute_1_pref: "Connaissance en " - archmage_attribute_1_suf: ", ou le désir d'apprendre. La plupart de notre code est écrit avec ce langage. Si vous êtes fan de Ruby ou Python, vous vous sentirez chez vous. C'est du JavaScript, mais avec une syntaxe plus sympatique." - archmage_attribute_2: "De l'expérience en développement et en initiatives personnelles. Nous vous aiderons à vous orienter, mais nous ne pouvons pas passer plus de temps à vous entrainer." + archmage_attribute_1_suf: ", ou le désir d'apprendre. La plupart de notre code est écrit avec ce langage. Si vous êtes fan de Ruby ou Python, vous vous sentirez chez vous. C'est du JavaScript, mais avec une syntaxe plus sympathique." + archmage_attribute_2: "De l'expérience en développement et en initiatives personnelles. Nous vous aiderons à vous orienter, mais nous ne pouvons pas passer plus de temps à vous entraîner." how_to_join: "Comment nous rejoindre" - join_desc_1: "N'importe qui peut aider! Vous avez seulement besoin de regarder " - join_desc_2: "pour commencer, et cochez la case ci-dessous pour vous marquer comme un courageux archimage et obtenir les dernières nouvelles par email. Envie de discuter de ce qu'il y a à faire ou de comment être plus impliqué? " + join_desc_1: "N'importe qui peut aider ! Vous avez seulement besoin de regarder " + join_desc_2: "pour commencer, et cochez la case ci-dessous pour vous marquer comme un courageux archimage et obtenir les dernières nouvelles par e-mail. Envie de discuter de ce qu'il y a à faire ou de comment être plus impliqué ? " join_desc_3: ", ou trouvez-nous dans nos " - join_desc_4: "et nous partirons de là!" - join_url_email: "Contactez nous" + join_desc_4: "et nous partirons de là !" + join_url_email: "Contactez-nous" join_url_hipchat: "conversation publique HipChat" - more_about_archmage: "En apprendre plus sur devenir un puissant archimage" - archmage_subscribe_desc: "Recevoir un email sur les nouvelles possibilités de développement et des annonces." - artisan_summary_pref: "Vous voulez concevoir des niveaux et élargir l'arsenal de CodeCombat? Les gens jouent à travers notre contenu à un rythme plus rapide que nous ne pouvons le construire! À l'heure actuelle, notre éditeur de niveau est basique, méfiez-vous. Concevoir des niveaux sera un peu difficile et buggé. Si vous avez des visions de campagnes couvrant les boucles 'for' pour" - artisan_summary_suf: ", alors ce travail est pour vous" - artisan_introduction_pref: "Nous devons créer des niveaux additionnels! Les gens veulent plus de contenu, et nous ne pouvons pas tous les créer nous-mêmes. Maintenant votre station de travail est au niveau un ; notre éditeur de niveaux est à peine utilisable même par ses créateurs, donc méfiez-vous. Si vous avez des idées sur la boucle for de" + archmage_subscribe_desc: "Recevoir un e-mail sur les nouvelles possibilités de développement et des annonces." + artisan_introduction_pref: "Nous devons créer des niveaux additionnels ! Les gens veulent plus de contenu, et nous ne pouvons pas tous les créer nous-même. Maintenant votre station de travail est au niveau un ; notre éditeur de niveaux est à peine utilisable même par ses créateurs, donc méfiez-vous. Si vous avez des idées sur la boucle for de" artisan_introduction_suf: ", cette classe est faite pour vous." - artisan_attribute_1: "Une expérience dans la création de contenu comme celui-ci serait un plus, comme utiliser l'éditeur de niveaux de Blizzard. Mais ce n'est pas nécessaire!" + artisan_attribute_1: "Une expérience dans la création de contenu comme celui-ci serait un plus, comme utiliser l'éditeur de niveaux de Blizzard. Mais ce n'est pas nécessaire !" artisan_attribute_2: "Vous aspirez à faire beaucoup de tests et d'itérations. Pour faire de bons niveaux, vous aurez besoin de les proposer aux autres et les regarder les jouer, et être prêt à trouver un grand nombre de choses à corriger." - artisan_attribute_3: "Pour l'heure, endurance en binôme avec un Aventurier. Notre éditeur de niveaux est vraiment préliminaire et frustrant à l'utilisation. Vous êtes prévenus!" + artisan_attribute_3: "Pour l'heure, endurance en binôme avec un Aventurier. Notre éditeur de niveaux est vraiment préliminaire et frustrant à l'utilisation. Vous êtes prévenus !" artisan_join_desc: "Utilisez le créateur de niveaux pour à peu près ces étapes :" artisan_join_step1: "Lire la documentation." - artisan_join_step2: "Créé un nouveau niveau et explore les niveaux existants." - artisan_join_step3: "Retrouvez nous dans notre conversation HipChat pour obtenir de l'aide." + artisan_join_step2: "Crééz un nouveau niveau et explorez les niveaux existants." + artisan_join_step3: "Retrouvez-nous dans notre conversation HipChat pour obtenir de l'aide." artisan_join_step4: "Postez vos niveaux dans le forum pour avoir des retours." - more_about_artisan: "En apprendre plus sur comment devenir un Artisan créatif" - artisan_subscribe_desc: "Recevoir un email sur les annonces et mises à jour de l'éditeur de niveaux." - adventurer_summary: "Soyons clair à propos de votre rôle : vous êtes le tank. Vous allez subir beaucoup de dégâts. Nous avons besoin de gens pour essayer les nouveaux niveaux et aider à identifier comment améliorer les choses. La douleur sera énorme; faire de bons jeux est une longue tâche et personne n'y arrive du premier coup. Si vous pouvez résister et avez un gros score de constitution, alors cette classe est faite pour vous." + artisan_subscribe_desc: "Recevoir un e-mail sur les annonces et mises à jour de l'éditeur de niveaux." adventurer_introduction: "Soyons clair à propos de votre rôle : vous êtes le tank. Vous allez subir beaucoup de dégâts. Nous avons besoin de gens pour essayer les nouveaux niveaux et aider à identifier comment améliorer les choses. La douleur sera énorme; faire de bons jeux est une longue tâche et personne n'y arrive du premier coup. Si vous pouvez résister et avez un gros score de constitution, alors cette classe est faite pour vous." adventurer_attribute_1: "Une soif d'apprendre. Vous voulez apprendre à développer et nous voulons vous apprendre. Vous allez toutefois faire la plupart de l'apprentissage." adventurer_attribute_2: "Charismatique. Soyez doux mais exprimez-vous sur ce qui a besoin d'être amélioré, et faites des propositions sur comment l'améliorer." - adventurer_join_pref: "Soit faire équipe avec (ou recruter!) un artisan et travailler avec lui, ou cocher la case ci-dessous pour recevoir un email quand il y aura de nouveaux niveaux à tester. Nous parlons aussi des niveaux à réviser sur notre réseau" + adventurer_join_pref: "Soit faire équipe avec (ou recruter!) un artisan et travailler avec lui, ou cocher la case ci-dessous pour recevoir un e-mail quand il y aura de nouveaux niveaux à tester. Nous parlons aussi des niveaux à réviser sur notre réseau" adventurer_forum_url: "notre forum" - adventurer_join_suf: "si vous préférez être avertis ainsi, inscrivez-vous ici!" - more_about_adventurer: "En apprendre plus sur devenir un brave Aventurier" - adventurer_subscribe_desc: "Recevoir un email lorsqu'il y a de nouveaux niveaux à tester." - scribe_summary_pref: "CodeCombat ne va pas juste être un tas de niveaux. Il sera également une source de connaissances de programmation sur lesquelles les joueurs pourront se référer. De cette façon, chaque artisan peut créer un lien vers un article détaillé pour l'édification du lecteur: documentation semblable à ce que le " - scribe_summary_suf: " a construit. Si vous aimez expliquer les concepts de programmation, cette classe est pour vous." + adventurer_join_suf: "si vous préférez être avertis ainsi, inscrivez-vous ici !" + adventurer_subscribe_desc: "Recevoir un e-mail lorsqu'il y a de nouveaux niveaux à tester." scribe_introduction_pref: "CodeCombat n'est pas seulement un ensemble de niveaux. Il contiendra aussi des ressources pour la connaissance, un wiki des concepts de programmation que les niveaux pourront illustrer. Dans ce but, chaque Artisan pourra, au lieu d'avoir à décrire en détail ce qu'est un opérateur de comparaison, seulement lier son niveau à l'article qui le décrit et qui a été écrit pour aider les joueurs. Quelque chose dans le sens de ce que le " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " a développé. Si votre définition de l'amusement passe par le format Markdown, alors cette classe est pour vous." scribe_attribute_1: "Les compétences rédactionnelles sont quasiment la seule chose dont vous aurez besoin. Pas seulement la grammaire et l'orthographe, mais être également capable de lier des idées ensembles." contact_us_url: "Contactez-nous" - scribe_join_description: "parlez nous un peu de vous, de votre expérience en programmation et de quels sujets vous souhaitez traiter. Nous partirons de là!" - more_about_scribe: "En apprendre plus sur comment devenir un Scribe assidu" - scribe_subscribe_desc: "Recevoir un email sur les annonces d'écriture d'article." - diplomat_summary: "Il ya un grand intérêt de CodeCombat dans d'autres pays qui ne parlent pas l'anglais! Nous recherchons des traducteurs qui sont prêts à passer leur temps à traduire le corpus de mots du site pour que CodeCombat soit accessible partout dans le monde dès que possible. Si vous souhaitez aider à obtenir CodeCombat international, cette classe est faite pour vous." + scribe_join_description: "parlez-nous un peu de vous, de votre expérience en programmation et de quels sujets vous souhaitez traiter. Nous partirons de là !" + scribe_subscribe_desc: "Recevoir un e-mail sur les annonces d'écriture d'article." diplomat_introduction_pref: "Si nous avons appris quelque chose du " diplomat_launch_url: "lancement en octobre" - diplomat_introduction_suf: "c'est qu'il y a un intérêt considérable pour CodeCombat dans d'autres pays, particulièrement au Brésil! Nous créons une équipe de traducteurs pour changer une liste de mots en une autre pour que CodeCombat soit le plus accessible possible à travers le monde. Si vous souhaitez avoir un aperçu des prochains contenus et avoir les niveaux dans votre langue le plus tôt possible, alors cette classe est faite pour vous." - diplomat_attribute_1: "Des facilités en anglais et dans la langue que vous souhaitez traduire. Pour transmettre des idées complexes, il est important d'avoir une solide compréhension des deux!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." - diplomat_join_pref_github: "Trouvez le fichier de langue souhaité" + diplomat_introduction_suf: "c'est qu'il y a un intérêt considérable pour CodeCombat dans d'autres pays, particulièrement au Brésil ! Nous créons une équipe de traducteurs pour changer une liste de mots en une autre pour que CodeCombat soit le plus accessible possible à travers le monde. Si vous souhaitez avoir un aperçu des prochains contenus et avoir les niveaux dans votre langue le plus tôt possible, alors cette classe est faite pour vous." + diplomat_attribute_1: "Des facilités en anglais et dans la langue que vous souhaitez traduire. Pour transmettre des idées complexes, il est important d'avoir une solide compréhension des deux !" + diplomat_i18n_page_prefix: "Vous pouvez commencer à traduire nos niveaux en allant sur notre " + diplomat_i18n_page: "page de traduction" + diplomat_i18n_page_suffix: ", ou notre interface et le site Web sur GitHub." + diplomat_join_pref_github: "Trouvez le fichier de langue souhaité " diplomat_github_url: "sur GitHub" - diplomat_join_suf_github: ", modifiez en ligne, et soumettez des requètes. Cochez aussi cette case ci-dessous pour vous tenir à jour sur les nouveaux développements d'internationalisation!" - more_about_diplomat: "En apprendre plus sur comment devenir un bon diplomate" - diplomat_subscribe_desc: "Recevoir un email sur le développement i18n et les niveaux à traduire." - ambassador_summary: "Nous essayons de construire une communauté, et chaque communauté a besoin d'une équipe de soutien quand il y a des problèmes. Nous avons des chats, e-mails et les réseaux sociaux afin que nos utilisateurs puissent se familiariser avec le jeu. Si vous voulez aider les gens à participer, à avoir du plaisir, et à apprendre un peu de programmation, cette classe est faite pour vous." - ambassador_introduction: "C'est la communauté que nous construisons, et vous en êtes les connexions. Nous avons des discussions via le chat Olark, emails et les réseaux sociaux avec plusieurs personnes, et l'aide vient à la fois du jeu lui-même et grâce à lui. Si vous voulez aider les gens, prendre part à l'aventure et vous amuser, avec un bon feeling de CodeCombat et ce vers quoi nous allons, alors cette classe est faite pour vous." - ambassador_attribute_1: "Compétences en communication. Être capable d'identifier les problèmes que les joueurs ont et les aider à les résoudre. Mais aussi nous tenir informés de ce que les joueurs disent, ce qu'ils aiment et n'aiment pas et d'autres choses de ce genre!" - ambassador_join_desc: "parlez-nous un peu de vous, de ce que vous avez fait et ce qui vous aimeriez faire. Nous partirons de ça!" + diplomat_join_suf_github: ", modifiez en ligne, et soumettez des requêtes. Cochez aussi cette case ci-dessous pour vous tenir à jour sur les nouveaux développements d'internationalisation !" + diplomat_subscribe_desc: "Recevoir un e-mail sur le développement i18n et les niveaux à traduire." + ambassador_introduction: "C'est la communauté que nous construisons, et vous en êtes les connexions. Nous avons des discussions via le chat Olark, e-mails et les réseaux sociaux avec plusieurs personnes, et l'aide vient à la fois du jeu lui-même et grâce à lui. Si vous voulez aider les gens, prendre part à l'aventure et vous amuser, avec un bon feeling de CodeCombat et ce vers quoi nous allons, alors cette classe est faite pour vous." + ambassador_attribute_1: "Compétences en communication. Être capable d'identifier les problèmes que les joueurs ont et les aider à les résoudre. Mais aussi nous tenir informés de ce que les joueurs disent, ce qu'ils aiment et n'aiment pas et d'autres choses de ce genre !" + ambassador_join_desc: "parlez-nous un peu de vous, de ce que vous avez fait et ce qui vous aimeriez faire. Nous partirons de ça !" ambassador_join_note_strong: "Note" - ambassador_join_note_desc: "Une de nos priorités est de développer un jeu multijoueur où les joueurs qui ont du mal à réussir un niveau peuvent demander de l'aide à un joueur de plus haut niveau. Ce sera un bon moyen pour que les ambassadeurs fassent leur travail. Nous vous garderons en ligne!" - more_about_ambassador: "En apprendre plus sur comment devenir un serviable Ambassadeur" - ambassador_subscribe_desc: "Recevoir un email sur les mises à jour de l'aide et les développements multijoueur." + ambassador_join_note_desc: "Une de nos priorités est de développer un jeu multijoueur où les joueurs qui ont du mal à réussir un niveau peuvent demander de l'aide à un joueur de plus haut niveau. Ce sera un bon moyen pour que les ambassadeurs fassent leur travail. Nous vous garderons en ligne !" + ambassador_subscribe_desc: "Recevoir un e-mail sur les mises à jour de l'aide et les développements multijoueur." changes_auto_save: "Les changements sont sauvegardés automatiquement quand vous changez l'état des cases à cocher." diligent_scribes: "Nos Scribes assidus :" powerful_archmages: "Nos puissants Archimages :" @@ -696,14 +1000,14 @@ module.exports = nativeDescription: "français", englishDescription: "French", t helpful_ambassadors: "Nos serviables Ambassadeurs :" ladder: - please_login: "Identifie toi avant de jouer à un ladder game." - my_matches: "Mes Matchs" + please_login: "Identifie-toi avant de jouer à un ladder game." + my_matches: "Mes matchs" simulate: "Simuler" - simulation_explanation: "En simulant une partie, tu peux classer ton rang plus rapidement!" - simulate_games: "Simuler une Partie!" + simulation_explanation: "En simulant une partie, tu peux classer ton rang plus rapidement !" + simulate_games: "Simuler une partie !" simulate_all: "REINITIALISER ET SIMULER DES PARTIES" - games_simulated_by: "Parties que vous avez simulé :" - games_simulated_for: "parties simulées pour vous :" + games_simulated_by: "Parties que vous avez simulées :" + games_simulated_for: "Parties simulées pour vous :" games_simulated: "Partie simulée" games_played: "Parties jouées" ratio: "Moyenne" @@ -713,88 +1017,120 @@ module.exports = nativeDescription: "français", englishDescription: "French", t summary_matches: "Matchs - " summary_wins: " Victoires, " summary_losses: " Défaites" - rank_no_code: "Nouveau Code à Classer" - rank_my_game: "Classer ma Partie!" + rank_no_code: "Nouveau code à classer" + rank_my_game: "Classer ma partie !" rank_submitting: "Soumission en cours..." - rank_submitted: "Soumis pour le Classement" - rank_failed: "Erreur lors du Classement" - rank_being_ranked: "Partie en cours de Classement" + rank_submitted: "Soumis pour le classement" + rank_failed: "Erreur lors du classement" + rank_being_ranked: "Partie en cours de classement" rank_last_submitted: "Envoyé " help_simulate: "De l'aide pour simuler vos parties" - code_being_simulated: "Votre nouveau code est en cours de simulation par les autres joueurs pour le classement. Cela va se rafraichir lors que d'autres matchs auront lieu." + code_being_simulated: "Votre nouveau code est en cours de simulation par les autres joueurs pour le classement. Cela va se rafraîchir lorsque d'autres matchs auront lieu." no_ranked_matches_pre: "Pas de match classé pour l'équipe " no_ranked_matches_post: "! Affronte d'autres compétiteurs et reviens ici pour classer ta partie." choose_opponent: "Choisir un Adversaire" - select_your_language: "Selectionnez votre langage!" - tutorial_play: "Jouer au Tutoriel" - tutorial_recommended: "Recommendé si tu n'as jamais joué avant" - tutorial_skip: "Passer le Tutoriel" - tutorial_not_sure: "Pas sûr de ce qu'il se passe?" - tutorial_play_first: "Jouer au Tutoriel d'abord." + select_your_language: "Selectionnez votre langage !" + tutorial_play: "Jouer au tutoriel" + tutorial_recommended: "Recommandé si tu n'as jamais joué avant" + tutorial_skip: "Passer le tutoriel" + tutorial_not_sure: "Pas sûr de ce qu'il se passe ?" + tutorial_play_first: "Jouer au tutoriel d'abord." simple_ai: "IA simple" warmup: "Échauffement" friends_playing: "Amis en train de jouer" - log_in_for_friends: "Connectez vous pour jouer avec vos amis!" - social_connect_blurb: "Connectez vous pour jouer contre vos amis!" - invite_friends_to_battle: "Invitez vos amis à rejoindre la bataille!" + log_in_for_friends: "Connectez-vous pour jouer avec vos amis !" + social_connect_blurb: "Connectez-vous pour jouer contre vos amis !" + invite_friends_to_battle: "Invitez vos amis à rejoindre la bataille !" fight: "Combattez !" watch_victory: "Regardez votre victoire" defeat_the: "Vaincre le" + tournament_started: ", a démarré" tournament_ends: "Fin du tournoi" tournament_ended: "Tournoi terminé" tournament_rules: "Règles du tournoi" tournament_blurb: "Écrire du code, collecter de l'or, construire des armées, écraser les ennemis, gagner des prix, et mettre à jour votre carrière dans notre tournoi $ 40,000 Greed! Découvrez les détails" - tournament_blurb_criss_cross: "Gagnez des offres, construisez des chemins, déjouez les adversaires, emparez-vous de gemmes, et améliorez votre carrière dans notre tournoi Criss-Cross! Découvrez les détails" + tournament_blurb_criss_cross: "Gagnez des offres, construisez des chemins, déjouez les adversaires, emparez-vous des gemmes, et améliorez votre carrière dans notre tournoi Criss-Cross! Découvrez les détails" + tournament_blurb_zero_sum: "Libérez votre créativité en programmation dans l'accumulation d'or et de tactiques de combat dans ce combat épique entre le sorcier rouge et bleu. Le tournoi a commencé vendredi le 27 mars et se déroulera jusqu'à lundi le 6 avril à 5PM PDT. Battez-vous pour le plaisir et l'honneur! Aller voir les détails." tournament_blurb_blog: "Sur notre blog" rules: "Règles" winners: "Gagnants" user: -# stats: "Stats" + stats: "Stats" singleplayer_title: "Niveaux solo" multiplayer_title: "Niveaux multijoueurs" achievements_title: "Succès" last_played: "Dernièrement joués" -# status: "Status" + status: "Statut" status_completed: "Terminé" status_unfinished: "Non terminé" - no_singleplayer: "Aucunes parties jouées pour le moment" - no_multiplayer: "Aucunes parties multijoueur pour le moment" - no_achievements: "Pas de succès encore gagnés." - favorite_prefix: "Langage favori :" -# favorite_postfix: "." + no_singleplayer: "Aucune partie jouée pour le moment" + no_multiplayer: "Aucune partie multijoueur pour le moment" + no_achievements: "Aucun succès gagné pour le moment." + favorite_prefix: "Langage favori : " + favorite_postfix: "." + not_member_of_clans: "Ne fais partie d'aucun clan pour l'instant." achievements: last_earned: "Dernièrement gagné" amount_achieved: "Quantité" achievement: "Succès" category_contributor: "Contributeur" -# category_ladder: "Ladder" -# category_level: "Level" + category_ladder: "Echelon" + category_level: "Niveau" category_miscellaneous: "Divers" category_levels: "Niveaux" category_undefined: "Non classé" -# current_xp_prefix: "" + current_xp_prefix: "" current_xp_postfix: " au total" -# new_xp_prefix: "" + new_xp_prefix: "" new_xp_postfix: " gagné" -# left_xp_prefix: "" + left_xp_prefix: "" left_xp_infix: " jusqu'au niveau " -# left_xp_postfix: "" + left_xp_postfix: "" account: - recently_played: "Jouées récemment" - no_recent_games: "Aucunes parties jouées au cours des deux dernières semaines." + recently_played: "Joué récemment" + no_recent_games: "Aucune partie jouée au cours des deux dernières semaines." + payments: "Paiements" + purchased: "Acheté" + subscription: "Souscrit" + invoices: "Factures" + service_apple: "Apple" + service_web: "Web" + paid_on: "Payé" + service: "Service" + price: "Prix" + gems: "Gemmes" + active: "Actif" + subscribed: "Inscrit" + unsubscribed: "Désincrit" + active_until: "Actif jusqu'à" + cost: "Coût" + next_payment: "Prochain paiement" + card: "Carte" + status_unsubscribed_active: "Vous n'êtes pas inscrit et ne serez pas facturé, mais votre compte est toujours actif." + status_unsubscribed: "Obtenez l'accès à de nouveaux niveaux, héros, objets et gemmes en bonus avec une inscription à CodeCombat !" + + account_invoices: + amount: "Montant (Dollars US)" + declined: "Votre carte a été refusée" + invalid_amount: "Entrez un montant en dollars US." + not_logged_in: "Connectez vous ou créez un compte pour accéder aux factures." + pay: "Paiement de facture" + purchasing: "Achat..." + retrying: "Erreur interne, réessayez" + success: "Paiement accepté, Merci !" loading_error: could_not_load: "Erreur de chargement du serveur" - connection_failure: "La connexion a échouée." - unauthorized: "Vous devez être identifiée pour faire cela. Avez-vous désactivé les cookies ?" + connection_failure: "La connexion a échoué." + unauthorized: "Vous devez être identifié pour faire cela. Avez-vous désactivé les cookies ?" forbidden: "Vous n'avez pas la permission." not_found: "Introuvable." not_allowed: "Méthode non autorisée." - timeout: "Connexion au serveur écoulée." - conflict: "Conflit de Ressources." + timeout: "Délai de connexion au serveur écoulé." + conflict: "Conflit de ressources." bad_input: "Données incorrectes ." server_error: "Erreur serveur." unknown: "Erreur inconnue." @@ -807,21 +1143,22 @@ module.exports = nativeDescription: "français", englishDescription: "French", t facebook_status: "Statut Facebook" facebook_friends: "Amis Facebook" facebook_friend_sessions: "Sessions d'amis Facebook" - gplus_friends: "Amis G+" - gplus_friend_sessions: "Sessions d'amis G+" + gplus_friends: "Amis Google+" + gplus_friend_sessions: "Sessions d'amis Google+" leaderboard: "Classement" user_schema: "Schéma d'utilisateur" - user_profile: "Profile d'utilisateur" + user_profile: "Profil d'utilisateur" + patch: "Patch" patches: "Patchs" - patched_model: "Document Source" + patched_model: "Document source" model: "Modèle" system: "Système" systems: "Systèmes" component: "Composant" components: "Composants" -# thang: "Thang" -# thangs: "Thangs" - level_session: "Votre Session" + thang: "Thang" + thangs: "Thangs" + level_session: "Votre session" opponent_session: "Session de l'adversaire" article: "Article" user_names: "Nom d'utilisateur" @@ -838,24 +1175,51 @@ module.exports = nativeDescription: "français", englishDescription: "French", t user_remarks: "Remarques des utilisateurs" versions: "Versions" items: "Objets" + hero: "Héros" heroes: "Héros" - wizard: "Magicien" achievement: "Succès" -# clas: "CLAs" + clas: "CLAs" play_counts: "Nombre de parties" feedback: "Réaction" + payment_info: "Info paiement" + campaigns: "Campagnes" + poll: "Sondage" + user_polls_record: "Historique de sondage des utilisateurs" + + concepts: + advanced_strings: "Chaînes de caractères avancés" + algorithms: "Algorithmes" + arguments: "Paramètres" + arithmetic: "Arithmétique" + arrays: "Tableaux" + basic_syntax: "Syntaxe basique" + boolean_logic: "Logique Booléenne" + break_statements: "Déclarations de sortie" + classes: "Classes" + for_loops: "Bloucles Pour" + functions: "Fonctions" + if_statements: "Déclarations conditionnelles Si" + input_handling: "Manipulation des entrées" + math_operations: "Opérations mathématiques" + object_literals: "Objets littéraux" + strings: "Chaînes de caractères" + variables: "Variables" + vectors: "Vecteurs" + while_loops: "Boucles Tant que" + recursion: "Récursivité" delta: added: "Ajouté" modified: "Modifié" +# not_modified: "Not Modified" deleted: "Supprimé" moved_index: "Index changé" text_diff: "Différence de texte" merge_conflict_with: "Fusionner les conflits avec" - no_changes: "Aucuns Changements" + no_changes: "Aucuns changements" -# guide: -# temp: "Temp" + guide: + temp: "Temp" multiplayer: multiplayer_title: "Préférences multijoueurs" # We'll be changing this around significantly soon. Until then, it's not important to translate. @@ -863,36 +1227,30 @@ module.exports = nativeDescription: "français", englishDescription: "French", t multiplayer_toggle_description: "Permettre à d'autres de rejoindre votre partie." multiplayer_link_description: "Partage ce lien pour que tes amis viennent jouer avec toi." multiplayer_hint_label: "Astuce:" - multiplayer_hint: " Cliquez sur le lien pour tout sélectionner, puis appuyer sur Pomme-C ou Ctrl-C pour copier le lien." + multiplayer_hint: " Cliquez sur le lien pour tout sélectionner, puis appuyez sur Pomme-C ou Ctrl-C pour copier le lien." multiplayer_coming_soon: "Plus de fonctionnalités multijoueurs sont à venir" multiplayer_sign_in_leaderboard: "Connectez-vous ou créez un compte et obtenez votre solution sur le classement." legal: - page_title: "Légal" + page_title: "Mentions légales" opensource_intro: "CodeCombat est complètement gratuit et open source." opensource_description_prefix: "Regardez " github_url: "notre GitHub" - opensource_description_center: "et aidez nous si vous voulez! CodeCombat est construit sur plusieurs projets open source, et nous les aimons. Regardez " + opensource_description_center: "et aidez nous si vous voulez ! CodeCombat est construit sur plusieurs projets open source, et nous les aimons. Regardez " archmage_wiki_url: "notre wiki des Archimages" opensource_description_suffix: "pour trouver la liste des logiciels qui rendent ce jeu possible." practices_title: "Bonnes pratiques" - practices_description: "Ce sont les promesses que nous vous faisons à vous, le joueur, en jargon un peu juridique." + practices_description: "Ce sont les promesses que nous vous faisons à vous, les joueurs, en jargon un peu juridique." privacy_title: "Vie privée" - privacy_description: "Nous ne vendrons aucune de vos informations personnelles. Nous comptons faire de l'argent éventuellement avec le recrutement, mais soyez assuré que nous ne fournirons aucune de vos informations personnelles à des compagnies intéressées sans votre consentement explicite." + privacy_description: "Nous ne vendons aucune de vos informations personnelles." security_title: "Sécurité" security_description: "Nous faisons tout notre possible pour conserver la confidentialité de vos informations personnelles. En tant que projet open source, notre site est ouvert à tous ceux qui souhaitent examiner et améliorer nos systèmes de sécurité." - email_title: "Email" - email_description_prefix: "Nous ne vous innonderons pas d'email. Grâce à" - email_settings_url: "vos paramètres d'email " - email_description_suffix: "ou avec des liens disponibles dans nos emails, vous pouvez changer vos préférences ou vous désinscrire à tout moment." + email_title: "E-mail" + email_description_prefix: "Nous ne vous innonderons pas d'e-mail. Grâce à" + email_settings_url: "vos paramètres d'e-mail " + email_description_suffix: "ou avec des liens disponibles dans nos e-mails, vous pouvez changer vos préférences ou vous désinscrire à tout moment." cost_title: "Coût" - cost_description: "Pour l'instant, CodeCombat est gratuit à 100%! Un de nos principaux objectifs est que ça le reste, pour qu'autant de gens possible puissent y jouer, indépendamment de leur niveau de vie. Si le ciel s'assombrit, nous devrons peut-être rendre les inscriptions payantes ou une partie du contenu, mais nous ne le souhaitons pas. Avec un peu de chance, nous serons capables de soutenir l'entreprise avec :" - recruitment_title: "Recrutement" - recruitment_description_prefix: "Ici chez CodeCombat, vous allez devenir un magicien puissant, pas seulement dans le jeu, mais aussi dans la vie réelle." - url_hire_programmers: "Personne ne peut recruter des développeurs aussi vite" - recruitment_description_suffix: "donc une fois que vous aurez aiguisé votre savoir-faire et si vous l'acceptez, nous montrerons vos meilleurs bouts de code aux milliers d'employeurs qui attendent une chance de vous recruter. Ils nous payent un peu pour ensuite vous payer" - recruitment_description_italic: "beaucoup" - recruitment_description_ending: "le site reste gratuit et tout le monde est content. C'est le but." + cost_description: "Pour l'instant, CodeCombat est gratuit à 100% ! Un de nos principaux objectifs est que ça le reste, pour qu'autant de gens possible puissent y jouer, indépendamment de leur niveau de vie. Si le ciel s'assombrit, nous devrons peut-être rendre les inscriptions payantes ou une partie du contenu, mais nous ne le souhaitons pas. Avec un peu de chance, nous serons capables de soutenir l'entreprise avec :" copyrights_title: "Copyrights et Licences" contributor_title: "Contributor License Agreement" contributor_description_prefix: "Toute contribution, sur le site et sur le répertoire GitHub, est sujette à nos" @@ -911,11 +1269,11 @@ module.exports = nativeDescription: "français", englishDescription: "French", t art_artwork: "Les artworks" art_sprites: "Les sprites" art_other: "Tout le reste du contenu non-code qui est rendu accessible lors de la création de niveaux." - art_access: "Pour l'instant il n'y a aucun système universel et facile pour rassembler ces ressources. En général, accédez y par les URL comme le fait le site, contactez-nous pour de l'aide, ou aidez-nous à agrandir le site pour rendre ces ressources plus facilement accessibles." - art_paragraph_1: "Pour l'attribution, s'il vous plait, nommez et référencez codecombat.com près de la source utilisée ou dans un endroit approprié. Par exemple:" + art_access: "Pour l'instant il n'y a aucun système universel et facile pour rassembler ces ressources. En général, accédez-y par les URL comme le fait le site, contactez-nous pour de l'aide, ou aidez-nous à agrandir le site pour rendre ces ressources plus facilement accessibles." + art_paragraph_1: "Pour l'attribution, s'il vous plaît, nommez et référencez codecombat.com près de la source utilisée ou dans un endroit approprié. Par exemple:" use_list_1: "Si utilisé dans un film ou un autre jeu, incluez codecombat.com dans le générique." use_list_2: "Si utilisé sur un site web, incluez un lien près de l'utilisation, par exemple sous une image, ou sur une page d'attribution générale où vous pourrez aussi mentionner les autres travaux en Creative Commons et les logiciels open source utilisés sur votre site. Quelque chose qui fait clairement référence à CodeCombat, comme un article de blog mentionnant CodeCombat, n'a pas besoin d'attribution séparée." - art_paragraph_2: "Si le contenu utilisé n'est pas créé par CodeCombat mais par un autre utilisateur de codecombat.com, attribuez le à cet utilisateur, et suivez les recommandations fournies dans la ressource de la description s'il y en a." + art_paragraph_2: "Si le contenu utilisé n'est pas créé par CodeCombat mais par un autre utilisateur de codecombat.com, attribuez-le à cet utilisateur, et suivez les recommandations fournies dans la ressource de la description s'il y en a." rights_title: "Droits réservés" rights_desc: "Tous les droits sont réservés pour les niveaux eux-mêmes. Cela inclut" rights_scripts: "Les scripts" @@ -933,7 +1291,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t blurb_1: "Ces prix seront décernés à" blurb_2: "Régles du tournoi" blurb_3: "aux meilleurs joueurs humains et ogre." - blurb_4: "Deux équipes, les prix doubles!" + blurb_4: "Deux équipes, les prix doubles !" blurb_5: "(Il y aura deux gagnants en première place, deux gagnants en deuxième place, etc.)" rank: "Rang" prizes: "Prix" @@ -943,47 +1301,31 @@ module.exports = nativeDescription: "français", englishDescription: "French", t custom_avatar: "Avatar CodeCombat personnalisé" # heap: "for six months of \"Startup\" access" credits: "Crédits" - one_month_coupon: "coupon: choisir eentre Rails ou HTML" + one_month_coupon: "coupon: choisir entre Rails ou HTML" one_month_discount: "30% de réduction: choisir entre Rails ou HTML" license: "Licence" oreilly: "ebook de votre choix" - wizard_settings: - title: "Paramètres du Magicien" - customize_avatar: "Personnaliser votre avatar" - active: "Actif" - color: "Couleur" - group: "Groupe" - clothes: "Vêtements" - trim: "Tailleur" - cloud: "Nuage" - team: "Équipe" - spell: "Sort" - boots: "Bottes" - hue: "Teinte" - saturation: "Saturation" - lightness: "Luminosité" - account_profile: settings: "Paramètres" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Editer Profil" - done_editing: "Modifications effectué" + done_editing: "Modifications effectuées" profile_for_prefix: "Profil pour " profile_for_suffix: "" featured: "Complet" not_featured: "Incomplet" looking_for: "à la recherche de:" - last_updated: "Dernière Mise à jour:" + last_updated: "Dernière mise à jour:" contact: "Contact" active: "En recherche d'offres" inactive: "Ne recherche pas d'offres" - complete: "terminé" + complete: "Terminé" next: "Suivant" - next_city: "ville ?" - next_country: "choisissez votre pays." - next_name: "nom ?" - next_short_description: "résumez votre profil en quelques mots." - next_long_description: "décrivez le travail que vous cherchez." + next_city: "Ville ?" + next_country: "Choisissez votre pays." + next_name: "Nom ?" + next_short_description: "Résumez votre profil en quelques mots." + next_long_description: "Décrivez le travail que vous cherchez." next_skills: "listez au moins 5 compétances." next_work: "décrivez votre expérience professionnelle." next_education: "raconter votre scolarité." @@ -1074,35 +1416,35 @@ module.exports = nativeDescription: "français", englishDescription: "French", t deprecation_warning: "Nous nous concentrons sur les niveaux pour débutants au lieu de trouver des développeurs experts pour le moment." # hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. get_started: "Commencer" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" + already_screened: "Nous avons déjà examiné techniquement tous les candidats" + filter_further: ", mais vous pouvez filtrer davantage:" + filter_visa: "Visa" filter_visa_yes: "US Authorisé" filter_visa_no: "Non autorisé" -# filter_education_top: "Top School" + filter_education_top: "Ecole supérieure" filter_education_other: "Autre" - filter_role_web_developer: "Développeur Webr" + filter_role_web_developer: "Développeur Web" filter_role_software_developer: "Développeur logiciel" filter_role_mobile_developer: "Développeur mobile" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" + filter_experience: "Expérience" + filter_experience_senior: "Senior" + filter_experience_junior: "Junior" # filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" + filter_experience_student: "Étudiant" filter_results: "Résultats" start_hiring: "Commencer à embaucher." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." + reasons: "Trois raisons pour lesquelles vous devriez employer vos prochains employés avec nous." + everyone_looking: "Toutes les personnes ici sont en quête de leur nouvel emploi." + everyone_looking_blurb: "Oubliez environ 20% du taux de réponse de courriels Linkedin. Toutes les personnes que nous avons listé sur ce site veulent être trouvé pour un nouvel emploi et répondront à votre requête pour qu'ils puissent s'introduire." + weeding: "Soyez tranquille, nous avons effectué une première sélection pour vous." + weeding_blurb: "Tous les joueurs que nous avons listés ont été sélectionné pour leurs abilités techniques. Nous allons également effectuer des entrevues techniques au téléphone pour sélectionner les candidats et ajouter des notes à leurs profiles afin de vous sauver du temps." + pass_screen: "Ils vont passer votre section technique" + pass_screen_blurb: "Réviser le code de chaque candidat avant de l'envoyer aux employeurs. Un employeur trouve que 5 fois plus de nos développeurs passent leur section technique que ceux en provenance de Hacker News." make_hiring_easier: "Faire de mon embauche plus facile, s'il vous plaît." what: "Qu'est-ce que CodeCombat?" what_blurb: "CodeCombat est un jeu de programmation multijoueur par navigateur. Les Joueurs écrivent le code pour contrôler leurs troupes dans des batailles contre d'autres développeurs. Nous prenons en charge JavaScript, Python, Lua, Clojure, CoffeeScript, et Io." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." + cost: "Combien chargeons-nous ?" + cost_blurb: "Nous chargeons 15% sur la première année de salaire et offrons une garantie de 100% d'argent remis sur une période de 90 jours. Nous ne chargerons pas les candidats qui activement en train d'être interviewer dans votre compagnie." candidate_name: "Nom" candidate_location: "Localisation" candidate_looking_for: "Poste pour" @@ -1116,10 +1458,10 @@ module.exports = nativeDescription: "français", englishDescription: "French", t inactive_developers: "Développeurs inactifs" admin: -# av_espionage: "Espionage" # Really not important to translate /admin controls. - av_espionage_placeholder: "Email ou nom d'utilisateur" + av_espionage: "Espionnage" # Really not important to translate /admin controls. + av_espionage_placeholder: "E-mail ou nom d'utilisateur" av_usersearch: "Recherche d'utilisateurs" - av_usersearch_placeholder: "Email, nom d'utilisateur, nom, peu importe" + av_usersearch_placeholder: "E-mail, nom d'utilisateur, nom, peu importe" av_usersearch_search: "Recherche" av_title: "Vues d'administrateurs" av_entities_sub_title: "Entités" diff --git a/app/locale/gl.coffee b/app/locale/gl.coffee index 18a0ec771..f5b938a6b 100644 --- a/app/locale/gl.coffee +++ b/app/locale/gl.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr slogan: "Aprende a programar xogando" no_ie: "CodeCombat non funciona en Internet Explorer 8 ou anteriores. Sentímolo!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat non foi deseñado para dispositivos móbiles e pode que non funcione!" # Warning that shows up on mobile devices - play: "Xogar" # The big play button that just starts playing a level + play: "Xogar" # The big play button that opens up the campaign view. old_browser: "Vaia, o seu navegador é demasiado vello para executar CodeCombat. Sentímolo!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Pódeo tentar de todos modos, pero probablemente no vaia a funcionar." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." campaign: "Campaña" for_beginners: "Para principiantes" multiplayer: "Multixogador" # Not currently shown on home page @@ -16,7 +17,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr play: "Xogar" # The top nav bar entry where players choose which levels to play community: "Comunidade" editor: "Editor" - blog: "Blogue" + blog: "Blog" forum: "Foro" account: "Conta" profile: "Perfil" @@ -41,7 +42,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr diplomat_suggestion: title: "Axuda a traducir CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "Necesitamos das túas habilidades lingüisticas." - pitch_body: "Nos desenvolvemos CodeCombat en inglés, pero xa temos xogadores de todo o mundo. Moitos deles queren xogar en galego porque non falan inglés, así que si falas ambos idiomas, inscríbete como Diplomático e axuda a traducir a web e todos os niveis de CodeCombat ao galego." + pitch_body: "Nós desenvolvemos CodeCombat en inglés, pero xa temos xogadores de todo o mundo. Moitos deles queren xogar en galego porque non falan inglés, así que si falas ambos idiomas, inscríbete como Diplomático e axuda a traducir a web e todos os niveis de CodeCombat ao galego." missing_translations: "Mentras terminamos a traducción ao galego, verás en castelán ou en inglés as partes que non estén ainda dispoñibles." learn_more: "Aprende máis sobre o que é ser un Diplomático" subscribe_as_diplomat: "Suscríbete como Diplomático" @@ -56,62 +57,60 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details heroes: "Heroes" # Tooltip on hero shop button from /play achievements: "Logros" # Tooltip on achievement list button from /play account: "Conta" # Tooltip on account button from /play settings: "Axustes" # Tooltip on settings button from /play - next: "Seguente Heroe" # Go from choose hero to choose inventory before playing a level +# poll: "Poll" # Tooltip on poll button from /play + next: "Seguinte Heroe" # Go from choose hero to choose inventory before playing a level change_hero: "Seleccionar Heroe" # Go back from choose inventory to choose hero choose_inventory: "Equipar Obxectos" # buy_gems: "Buy Gems" - older_campaigns: "Campañas Anteriores" +# subscription_required: "Subscription Required" anonymous: "Xogador Anónimo" level_difficulty: "Dificultade: " campaign_beginner: "Campaña de Principiante" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Elixe o teu nivel" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Podes elixir calqueira pantalla ou falar no " - adventurer_forum: " foro do aventureiro " - adventurer_suffix: "sobre elo." - campaign_old_beginner: "Antiga Campaña de Principiante" - campaign_old_beginner_description: "... na que aprenderás a maxia da programación." - campaign_dev: "Niveis aleatorios máis dificiles" - campaign_dev_description: "... nos que aprenderás sobre a interface mentres fas algo máis difícil." +# adjust_volume: "Adjust volume" campaign_multiplayer: "Areas Multixogador" campaign_multiplayer_description: "... nas que o teu código se enfrentará ao de outros xogadores." - campaign_player_created: "Creacións dos Xogadores" - campaign_player_created_description: "... nas que loitas contra a creatividade dos teus compañeiros <a href=\"/contribute#artisa\">Magos Artesáns</a>." - campaign_classic_algorithms: "Algoritmos Clásicos" - campaign_classic_algorithms_description: "... donde aprendes os algoritmos máis populares da informatica." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "Crear unha conta" log_in: "Entrar" logging_in: "Entrando..." - log_out: "Sair" - recover: "Recuperar conta" + log_out: "Saír" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" signup: - create_account_title: "Crea unha conta para gardar o teu progreso" - description: "É de balde!. Só precisamos un par de cousas e xa podes comenzar!" email_announcements: "Recibir noticias por correo electrónico" - coppa: "Son maior de 13 anos ou de fora dos Estados Unidos" - coppa_why: "(Por que?)" creating: "Creando conta..." sign_up: "Rexistrarse" log_in: "Iniciar sesión con contrasinal" social_signup: "Ou, podes acceder a través da túa conta de Facebook ou G+:" - required: "Tes que estar rexistrado antes de poder seguir por aquí." + required: "Tes que estar rexistrado antes de poder continuar." +# login_switch: "Already have an account?" recover: recover_account_title: "Recuperar Conta" @@ -123,10 +122,12 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr # secondary: "Secondary" armor: "Armadura" accessories: "Accesorios" - misc: "Miscelanea" + misc: "Miscelánea" # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Cargando..." saving: "Gardando..." sending: "Enviando..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr fork: "Bifurcar" play: "Xogar" # When used as an action verb, like "Play next level" retry: "Reintentar" +# actions: "Actions" +# info: "Info" +# help: "Help" watch: "Mirar" unwatch: "Pasar" - submit_patch: "Mandar Parche" + submit_patch: "Enviar Parche" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" general: and: "e" @@ -149,16 +155,29 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr date: "Data" body: "Corpo" version: "Versión" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" commit_msg: "Mensaxe de Asignación ou Commit" +# review: "Review" version_history: "Historial de versión" version_history_for: "Historial das versións de: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" result: "Resultado" results: "Resultados" description: "Descripción" or: "ou" subject: "Asunto" email: "Correo electrónico" - password: "Password" + password: "Contrasinal" message: "Mensaxe" code: "Código" ladder: "Clasificación" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr hard: "Difícil" player: "Xogador" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" units: second: "segundo" @@ -184,7 +206,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr hours: "horas" day: "día" days: "días" - week: "semán" + week: "semana" weeks: "semanas" month: "mes" months: "meses" @@ -196,7 +218,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr home: "Inicio" # Not used any more, will be removed soon. # level: "Level" # Like "Level: Dungeons of Kithgard" skip: "Saltar" - game_menu: "Menú do Xogo" + game_menu: "Menú de Xogo" guide: "Guía" restart: "Reiniciar" goals: "Obxectivos" @@ -204,7 +226,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr # running: "Running..." success: "Éxito!" incomplete: "Incompleto" - timed_out: "Quedalles sin tiempo" + timed_out: "Acabouse o tempo" failing: "Fallando" action_timeline: "Cronoloxía de Acción" click_to_select: "Preme nunha unidade para seleccionala" @@ -212,40 +234,41 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr # control_bar_join_game: "Join Game" # reload: "Reload" reload_title: "Recargar todo o código?" - reload_really: "Estás seguro que queres reiniciar o nivel?" + reload_really: "Estás seguro de que queres reiniciar o nivel?" reload_confirm: "Recargalo todo" +# victory: "Victory" victory_title_prefix: "¡" victory_title_suffix: " Completado!" victory_sign_up: "Rexístrate para recibir actualizacións." - victory_sign_up_poke: "Queres recibir as últimas noticias no teu correo electrónico? Crea unha conta gratuita e manteremoste informado!" + victory_sign_up_poke: "Queres recibir as últimas noticias no teu correo electrónico? Crea unha conta gratuita e manterémoste informado!" victory_rate_the_level: "Puntúa este nivel: " # Only in old-style levels. victory_return_to_ladder: "Volver á Clasificación" victory_play_continue: "Continuar" - victory_play_skip: "Saltar animación" - victory_play_next_level: "Xogar o seguinte nivel" - victory_play_more_practice: "Máis Práctica" - victory_play_too_easy: "Demasiado doado" - victory_play_just_right: "Xusto" - victory_play_too_hard: "Demasiado dificil" victory_saving_progress: "Gardando Progreso" victory_go_home: "Ir ao Inicio" # Only in old-style levels. victory_review: "Contanos máis!" # Only in old-style levels. victory_hour_of_code_done: "Xa remataches?" - victory_hour_of_code_done_yes: "Si, rematei coa miña hora de código!" + victory_hour_of_code_done_yes: "Sí, rematei coa miña hora de código!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Guía" - tome_minion_spells: "Os feitizos dos de teus súbditos" # Only in old-style levels. - tome_read_only_spells: "Feitizos de so lectura" # Only in old-style levels. + tome_minion_spells: "Os feitizos dos teus súbditos" # Only in old-style levels. + tome_read_only_spells: "Feitizos de só lectura" # Only in old-style levels. tome_other_units: "Outras unidades" # Only in old-style levels. tome_cast_button_run: "Executar" tome_cast_button_running: "Executando" tome_cast_button_ran: "Executado" tome_submit_button: "Mandar" - tome_reload_method: "Recargcar código orixinal para este método" # Title text for individual method reload button. + tome_reload_method: "Recargar código orixinal para este método" # Title text for individual method reload button. tome_select_method: "Seleccionar método" - tome_see_all_methods: "Métodos que poden ser editados" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "Métodos que poden ser editados" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Selecciona a alguén para " tome_available_spells: "Feitizos dispoñibles" - tome_your_skills: "As Túas Habilidades" + tome_your_skills: "As túas Habilidades" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" code_saved: "Codigo Gardado" @@ -254,61 +277,103 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr loading_ready: "Listo!" loading_start: "Iniciar Nivel" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" time_current: "Agora:" time_total: "Máx:" time_goto: "Ir a:" - infinite_loop_try_again: "Tentao de novo" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "Téntao de novo" infinite_loop_reset_level: "Reiniciar nivel" infinite_loop_comment_out: "Comenta o meu código" tip_toggle_play: "Alterna entre xogar/pausa con Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ y Ctrl+] rebobina e avanza cara adiante." + tip_scrub_shortcut: "Ctrl+[ y Ctrl+] rebobina e avanza cara adiante." # {change} tip_guide_exists: "Preme na guía arriba da páxina para máis información útil." tip_open_source: "CodeCombat é 100% código aberto!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat lanzou a súa beta en Outubro de 2014." tip_think_solution: "Pensa na solución, non no problema." tip_theory_practice: "En teoría, non hai diferenza entre a teoría e a práctica. Pero na práctica, si que a hai. - Yogi Berra" tip_error_free: "Hai dúas formas de escribir programas sin erros; só a terceira funciona. - Alan Perlis" - tip_debugging_program: "Si depurar é o proceso de eliminar erros, entón programar debe ser o proceso de crearlos. - Edsger W. Dijkstra" + tip_debugging_program: "Si depurar é o proceso de eliminar erros, entón programar debe ser o proceso de crealos. - Edsger W. Dijkstra" tip_forums: "Diríxete aos foros e dinos o que pensas!" tip_baby_coders: "No futuro, incluso os bebés serán Archimagos." tip_morale_improves: "Seguirase cargando ata que a moral mellore." - tip_all_species: "Cremos nas mesmas oportunidades para aprender a programar para todas as especies." + tip_all_species: "Cremos nas mesmas oportunidades para aprender a programar para tódalas especies." tip_reticulating: "Recompoñendo o espiñazo." tip_harry: "Ei un mago, " - tip_great_responsibility: "Grandes habilidades de codificación programación conlevan unha gran responsabilidade á hora de depurar." - tip_munchkin: "Si non comes a verdura, un munchkin virá a por ti mentras dormes." - tip_binary: "Hai 10 tipos de personas no mundo: as que saben binario e as que non." + tip_great_responsibility: "As Grandes habilidades de codificación-programación conlevan unha gran responsabilidade á hora de depurar." + tip_munchkin: "Se non comes a verdura, un munchkin virá a por ti mentras durmes." + tip_binary: "Hai 10 tipos de persoas no mundo: as que saben binario e as que non." tip_commitment_yoda: "Un programador debe ter o máis serio compromiso, a mente máis crítica. ~ Ioda" tip_no_try: "Faino ou non o fagas, pero non o tentes. - Ioda" - tip_patience: "Paciencia ter debes, xoven Padawan. - Ioda" + tip_patience: "Paciencia ter debes, xove Padawan. - Ioda" tip_documented_bug: "Un erro documentado non é un erro, é unha característica máis." tip_impossible: "Sempre parece imposible, ata que se fai. - Nelson Mandela" - tip_talk_is_cheap: "Falar é doado. Ensiname o código. - Linus Torvalds" + tip_talk_is_cheap: "Falar é doado. Ensíname o código. - Linus Torvalds" tip_first_language: "A cousa máis desastrosa que podes aprender é a túa primeira linguaxe de programación. - Alan Kay" tip_hardware_problem: "P: Cantos programadores fan falla para cambiar unha bombilla? R: Ningún, é un problema de hardware." # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." tip_premature_optimization: "A optimizacion prematura é a raíz de todo mal. - Donald Knuth" - tip_brute_force: "Cando hai dúbidas, usa a forza bruta. - Ken Thompson" - customize_wizard: "Persoalizar Mago" + tip_brute_force: "Cando haxa dúbidas, usa a forza bruta. - Ken Thompson" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventario" save_load_tab: "Gardar/Cargar" options_tab: "Opcións" - guide_tab: "Guia" + guide_tab: "Guía" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" multiplayer_tab: "Multixogador" auth_tab: "Crear conta" - inventory_caption: "Equipa ao teu heroe" - choose_hero_caption: "Elixe a lingua do heroe" + inventory_caption: "Equipa ao teu Heroe" + choose_hero_caption: "Escolle a lingua do teu Heroe" save_load_caption: "... e mirar a historia" - options_caption: "Axustes de configuracion" + options_caption: "Axustes de configuración" guide_caption: "Documentos e pistas" - multiplayer_caption: "Xoga con amigos!" + multiplayer_caption: "Xoga cos teus amigos!" auth_caption: "Gardar o teu progreso." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + inventory: choose_inventory: "Equipar Obxectos" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,31 +390,118 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: - choose_hero: "Selecciona o teu Heroe" - programming_language: "Linguaxe de Programación" + choose_hero: "Selecciona ó teu Heroe" + programming_language: "Linguaxe de programación" programming_language_description: "Que linguaxe de programación desexas usar?" # default: "Default" # experimental: "Experimental" - python_blurb: "Simple pero poderoso." + python_blurb: "Simple, pero poderoso." javascript_blurb: "A linguaxe da web." - coffeescript_blurb: "Sintase de JavaScript mellorada." + coffeescript_blurb: "Síntese de JavaScript mellorada." clojure_blurb: "Un Lisp moderno." lua_blurb: "Linguaxe Script para Xogos." - io_blurb: "Simple pero oscuro." + io_blurb: "Simple, pero oscuro." status: "Estado" +# hero_type: "Type" weapons: "Armas" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" # weapons_wizard: "Wands, Staffs - Long Range, Magic" - attack: "Dano" # Can also translate as "Attack" + attack: "Ataque" # Can also translate as "Attack" health: "Saúde" speed: "Velocidade" # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" skills: "Habilidades" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -376,24 +528,22 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr options: general_options: "Opcións Xerais" # Check out the Options tab in the Game Menu while playing a level volume_label: "Volume" - music_label: "Musica" - music_description: "Musica de fondo on/off." - autorun_label: "Autorun" - autorun_description: "Control automatico de código en execución." + music_label: "Música" + music_description: "Música de fondo activada/desactivada." editor_config: "Conf. editor" editor_config_title: "Configuración do editor" editor_config_level_language_label: "Linguaxe para este nivel" - editor_config_level_language_description: "Escolle a linguaxe de programacion para este nivel en concreto." + editor_config_level_language_description: "Escolle a linguaxe de programación para este nivel en concreto." editor_config_default_language_label: "Linguaxe de programación por defecto" - editor_config_default_language_description: "Define a linguaxe de programación na que queres programar cando empiezes un novo nivel." + editor_config_default_language_description: "Define a linguaxe de programación na que queres programar cando comeces un novo nivel." editor_config_keybindings_label: "Atallos de teclado" editor_config_keybindings_default: "Actual (Ace)" - editor_config_keybindings_description: "Permite o uso de atallos de teclado de algúns editores coñecidos." + editor_config_keybindings_description: "Permite o uso de atallos de teclado dalgúns editores coñecidos." # editor_config_livecompletion_label: "Live Autocompletion" editor_config_livecompletion_description: "Amosa suxestións de autocompletado mentres se escribe." editor_config_invisibles_label: "Amosar elementos invisibles" - editor_config_invisibles_description: "Pódense ver elementos invisibles como espacios ou tabulacións." - editor_config_indentguides_label: "Amostrar guías de sangría" + editor_config_invisibles_description: "Pódense ver elementos invisibles como espazos ou tabulacións." + editor_config_indentguides_label: "Amosar guías de sangría" editor_config_indentguides_description: "Pódense ver as liñas verticais que definen o sangrado dunha forma máis clara." editor_config_behaviors_label: "Comportamentos intelixentes" editor_config_behaviors_description: "Complétanse automáticamente corchetes, paréntesis e comiñas." @@ -403,46 +553,140 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr why_paragraph_1: "Precisas aprender a programar? Non che fan falla leccións. O que precisas é escribir moitísimo código e pasalo ben facéndoo." why_paragraph_2_prefix: "De iso vai a programación. Ten que ser divertido. Non divertido como:" why_paragraph_2_italic: "ben unha medalla!," - why_paragraph_2_center: "sinón máis ben como:" + why_paragraph_2_center: "senón máis ben como:" why_paragraph_2_italic_caps: "NON MAMÁ, TEñO QUE REMATAR O NIVEL!" - why_paragraph_2_suffix: "Por eso Codecombat é multixogador, non un curso con leccións \"con dinámicas de xogo\" . Non pararemos ata que ti non poidas parar... pero esta vez, eso será bo sinal." - why_paragraph_3: "Si vas a engancharte a algún xogo, engánchate a este e convírtete nun dos magos da era tecnolóxica." - press_title: "Blogueros/Prensa" - press_paragraph_1_prefix: "Queres escribir sobre nós? Baixate e usa todos os recursos incluidos no noso" + why_paragraph_2_suffix: "Por iso Codecombat é multixogador, non un curso con leccións \"con dinámicas de xogo\" . Non pararemos ata que ti non poidas parar... mais por esta vez, iso será bo sinal." + why_paragraph_3: "Se vas a engancharte a algún xogo, engánchate a este e convírtete nun dos magos da era tecnolóxica." + press_title: "Blogueiros/Prensa" + press_paragraph_1_prefix: "Queres escribir sobre nós? Báixate e usa tódolos recursos incluidos no noso" press_paragraph_1_link: "paquete de prensa" - press_paragraph_1_suffix: ". Todos os logos e as imaxes poden ser usadas sin necesidade de contactarnos directamente." + press_paragraph_1_suffix: ". Todos os logos e as imaxes poden ser usadas sen necesidade de contactarnos directamente." team: "Equipo" - george_title: "CEO" - george_blurb: "Home de Negocios" - scott_title: "Programador" + george_title: "CEO" # {change} + george_blurb: "Sección de Negocios" + scott_title: "Programador" # {change} scott_blurb: "Razoable" - nick_title: "Programador" + nick_title: "Programador" # {change} nick_blurb: "Gurú Motivacional" michael_title: "Programador" michael_blurb: "Administrador de Sistemas" - matt_title: "Programador" + matt_title: "Programador" # {change} matt_blurb: "Ciclista" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Gardar nova versión" new_major_version: "Nova versión principal" +# submitting_patch: "Submitting Patch..." cla_prefix: "Para gardar os cambios, primeiro debes aceptar o noso" cla_url: "CLA" cla_suffix: "." cla_agree: "De acordo" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contacta con CodeCombat" welcome: "Gústanos saber de ti! Usa este formulario para enviarnos un correo. " - contribute_prefix: "Si estás interesado en colaborar, botalle un ollo á nosa " - contribute_page: "páxina de contribucións" - contribute_suffix: "!" forum_prefix: "Para asuntos públicos, por favor usa " forum_page: "o noso foro" forum_suffix: " no seu lugar." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Envía o teu comentario" contact_candidate: "Contactar Candidato" # Deprecated - recruitment_reminder: "Usa este formulario para contactar cos candidatos que queiras entrevistar. Recorda que CodeCombat cobrará o 18% do salario durante o primeiro ano. A cuota é pola contratación do empregado e é reembolsable durante 90 días si o empregado non permanece contratado. A tempo parcial, a distancia e os empregados de contrato son gratis, como o son os bolseiros." # Deprecated + recruitment_reminder: "Usa este formulario para contactar cos candidatos que queiras entrevistar. Recorda que CodeCombat cobrará o 18% do salario durante o primeiro ano. A cuota é pola contratación do empregado e é reembolsable durante 90 días se o empregado non permanece contratado. A tempo parcial, a distancia e os empregados de contrato son gratis, como o son os bolseiros." # Deprecated account_settings: title: "Axustes da conta" @@ -450,48 +694,54 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr autosave: "Os cambios gardaranse automáticamente" me_tab: "Eu" picture_tab: "Foto" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" upload_picture: "Sube unha imaxe" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Contrasinal" emails_tab: "Correos electrónicos" admin: "Admin" new_password: "Novo contrasinal" new_password_verify: "Verificar" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "Suscripcións de correo electrónico" - email_subscriptions_none: "Sin suscripcións de correo electrónico." + email_subscriptions_none: "Sen suscripcións de correo electrónico." email_announcements: "Novas" email_announcements_description: "Recibe correos electrónicos coas últimas novas e desenvolvementos de CodeCombat." email_notifications: "Notificacións" - email_notifications_summary: "Controles para persoalizar as notificacións antomáticas por correo electrónico, relacionadas coa túa actividade en CodeCombat." - email_any_notes: "Calqueira Notificacion" - email_any_notes_description: "Deshabilitar todas as notificacións por correo electrónico." + email_notifications_summary: "Controles para personalizar as notificacións antomáticas por correo electrónico, relacionadas coa túa actividade en CodeCombat." + email_any_notes: "Calqueira notificación" + email_any_notes_description: "Deshabilitar tódalass notificacións por correo electrónico." email_news: "Novas" email_recruit_notes: "Oportunidades de Traballo" - email_recruit_notes_description: "Si xogas realmente ben, pode que contactemos contigo para que consigas un traballo (mellor)." + email_recruit_notes_description: "Se xogas realmente ben, pode que contactemos contigo para que consigas un traballo (mellor)." contributor_emails: "Correos para colaboradores" - contribute_prefix: "Buscamos xente que se una á nosa comunidade! Comprobaa " - contribute_page: "páxina de colaboraciones" + contribute_prefix: "Buscamos xente que se una á nosa comunidade! Compróbaa " + contribute_page: "Páxina de colaboracións" contribute_suffix: " para saber máis." email_toggle: "Activar todo" - error_saving: "Error ao gardar" + error_saving: "Erro ao gardar" saved: "Cambios gardados" - password_mismatch: "O contrasinal no coincide" + password_mismatch: "O contrasinal non coincide" password_repeat: "Repite o teu contrasinal." job_profile: "Perfil de traballo" # Rest of this section (the job profile stuff and wizard stuff) is deprecated job_profile_approved: "O teu perfil de traballo foi aprobado por CodeCombat. Os empleadores poderán velo ata que o marques como inactivo ou non sexa cambiado durante catro semanas." - job_profile_explanation: "Oola! Enche isto e estaremos en contacto para falar sobre atoparche un traballo como desenvolvedor de software." + job_profile_explanation: "Ola! Enche isto e estaremos en contacto para falar sobre atoparte un traballo como desenvolvedor de software." sample_profile: "Mira un perfil de exemplo" view_profile: "Mira o teu perfil" - wizard_tab: "Mago" - wizard_color: "Cor da roupa do Mago" keyboard_shortcuts: keyboard_shortcuts: "Atallos de teclado" space: "Barra espaciadora (Espazo)" enter: "Enter" +# press_enter: "press enter" escape: "Escape" shift: "Shift" # run_code: "Run current code." - run_real_time: "executar en tempo real." + run_real_time: "Executar en tempo real." # continue_script: "Continue past current script." # skip_scripts: "Skip past all skippable scripts." # toggle_playback: "Toggle play/pause." @@ -502,8 +752,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr # toggle_grid: "Toggle grid overlay." # toggle_pathfinding: "Toggle pathfinding overlay." beautify: "Embelece o teu código estandarizando o formato." - maximize_editor: "Maximizar/minimizar editor de código." - move_wizard: "Mover ao teu feiticeiro polo nivel." + maximize_editor: "Maximizar/minimizar o editor de código." community: main_title: "Comunidade de CodeCombat" @@ -514,28 +763,78 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr # thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." # article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" # article_editor_suffix: "and help CodeCombat players get the most out of their playtime." - find_us: "Encontranos nestes sitios" - social_blog: "Lee o blogue de CodeCombat en Sett" + find_us: "Podes atoparnos nestes sitios" + social_blog: "Lee o blog de CodeCombat en Sett" social_discource: "Únete á discusion no noso foro" social_facebook: "Dalle a Gústame a CodeCombat en Facebook" social_twitter: "Segue a CodeCombat en Twitter" social_gplus: "Únete a CodeCombat en Google+" social_hipchat: "Fala con nós no chat público de CodeCombat HipChat room" - contribute_to_the_project: "Contribue ao proxecto" + contribute_to_the_project: "Contribúe ao proxecto" + +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" classes: archmage_title: "Archimago" archmage_title_description: "(Programador)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "Artesán" artisan_title_description: "(Deseñador de Niveis)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "Aventureiro" adventurer_title_description: "(Tester de Niveis)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Escriba" scribe_title_description: "(Editor de Artigos)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "Diplomático" - diplomat_title_description: "(Traductor)" + diplomat_title_description: "(Tradutor)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "Embaixador" ambassador_title_description: "(Soporte)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: main_title: "Editores de CodeCombat" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr thang_title: "Editor de Obxectos" level_title: "Editor de Niveis" achievement_title: "Editor de Logros" +# poll_title: "Poll Editor" back: "Voltar" revert: "Revertir" revert_models: "Revertir Modelos" pick_a_terrain: "Escolle un Terreno" - small: "Pequeno" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" grassy: "Cuberto de herba" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Pequeno" +# large: "Large" fork_title: "Bifurcar nova versión" fork_creating: "Creando bifurcación..." generate_terrain: "Xerar Terreo" more: "Máis" wiki: "Wiki" live_chat: "Chat en directo" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" level_some_options: "Algunhas opcións?" level_tab_thangs: "Obxectos" level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr level_tab_thangs_all: "Todo" level_tab_thangs_conditions: "Condicións de inicio" level_tab_thangs_add: "Engadir Obxectos" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" delete: "Borrar" duplicate: "Duplicar" +# stop_duplicate: "Stop Duplicate" rotate: "Rotar" level_settings_title: "Axustes" level_component_tab_title: "Compoñentes Actuais" @@ -575,128 +889,118 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr level_systems_tab_title: "Sistemas Actuais" level_systems_btn_new: "Crear Novo Sistema" level_systems_btn_add: "Engadir Sistema" - level_components_title: "Voltar a todos os Obxectos" + level_components_title: "Voltar a todos os obxectos" level_components_type: "Tipo" level_component_edit_title: "Editar Compoñente" level_component_config_schema: "Configurar esquema" level_component_settings: "Axustes" - level_system_edit_title: "Editar Sistema" - create_system_title: "Crear Novo Sistema" - new_component_title: "Crear Novo Compoñente" + level_system_edit_title: "Editar sistema" + create_system_title: "Crear novo sistema" + new_component_title: "Crear novo compoñente" new_component_field_system: "Sistema" - new_article_title: "Crear un novo artigo" - new_thang_title: "Crear un novo tipo de obxecto" - new_level_title: "Crear un novo nivel" - new_article_title_login: "Inicia sesión para Crear un Novo Artigo" - new_thang_title_login: "Inicia sesión para Crear un Novo Tipo de Thang" - new_level_title_login: "Inicia sesión para Crear un Novo Nivel" + new_article_title: "Crear un novo Artigo" + new_thang_title: "Crear un novo Tipo de obxecto" + new_level_title: "Crear un novo Nivel" + new_article_title_login: "Inicia sesión para crear un novo Artigo" + new_thang_title_login: "Inicia sesión para crear un novo Tipo de Thang" + new_level_title_login: "Inicia sesión para crear un novo Nivel" new_achievement_title: "Crear un novo Logro" - new_achievement_title_login: "Inicia sesión para Crear un Novo Logro" + new_achievement_title_login: "Inicia sesión para crear un novo Logro" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" article_search_title: "Buscar artigos aquí" thang_search_title: "Busca tipos de obxectos aquí" level_search_title: "Buscar niveis aquí" achievement_search_title: "Buscar Logros" +# poll_search_title: "Search Polls" read_only_warning2: "Nota: non podes gardar nada do que edites aquí porque non iniciaches sesión." - no_achievements: "No se engadiron logros a este nivel." + no_achievements: "Non se engadiron Logros a este nivel." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" - level_completion: "Porcentaxe de Nivel Completada" + level_completion: "Porcentaxe de Nivel completado" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Vista preliminar" edit_article_title: "Editar artigo" +# polls: +# priority: "Priority" + contribute: page_title: "Colaborar" - character_classes_title: "Clases de Personaxes" - introduction_desc_intro: "Temos moitas esperanzas en CodeCombat." - introduction_desc_pref: "Queremos estar donde programadores de todo tipo veñan a aprender a xogar xuntos, introducir a outros no maravilloso mundo da programación e reflexar a mellor parte da comunidade. Non podemos, nin queremos, facelo sos; o que fai grandes a proxectos como GitHub, Stack Overflow e Linux é a xente que os usa e crea con eles. A tal fin, " - introduction_desc_github_url: "CodeCombat é totalmente de código aberto" - introduction_desc_suf: ", e o noso obxectivo é ofrecer tantas maneiras como sexa posible para que tomes parte e fagas de este proxecto algo tan teu como noso." - introduction_desc_ending: "Agardamos que te unas ao noso equipo!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy y Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" alert_account_message_intro: "Ola!" - alert_account_message: "Para suscribirse aos correos electrónicos de clase, precisar estar na túa conta." - archmage_summary: "Interesado en traballar en gráficos para xogos, o deseño da interface de usuario, bases de datos e a organización de servidores, redes multixogador, físicas, son ou o funcionamiento do motor de xogo? Queres axudar a construir un xogo para axudar a outras persoas a aprender aquelo no que eres bo? Temos moito que facer e si eres un programador experimentado e queres desenvolver para CodeCombat, esta clase é para ti. Encantaríanos recibir a túa axuda para construir o mellor xogo de programación que se teña feito." - archmage_introduction: "Unha das mellores partes de desenvolver xogos é que combinan cousas moi diferentes. Gráficos, son, uso de redes en tempo real, redes sociais e por supuesto moitos dos aspectos comúns da programación, dende xestión de bases de datos a baixo nivel e administración de servidores ata deseño de experiencia do usuario e creación de interfaces. Hai unha morea de cousas por facere si eres un programador experimentado con interés en coñecer o que se coce na trastenda de CodeCombat, esta Clase pode ser la ideal para ti. Encantaríanos recibir a túa axuda para crear o mellor xogo de programación da historia." + alert_account_message: "Para suscribirse aos correos electrónicos de clase, precisas estar na túa conta." + archmage_introduction: "Unha das mellores partes de desenvolver xogos é que combinas cousas moi diferentes: gráficos, son, uso de redes en tempo real, redes sociais e, por suposto, moitos dos aspectos comúns da programación, dende xestión de bases de datos a baixo nivel e administración de servidores ata deseño de experiencia do usuario e creación de interfaces. Hai unha morea de cousas por facer e se eres un programador experimentado con interés en coñecer o que se coce na trastenda de CodeCombat, esta Clase pode ser a ideal para ti. Encantaríanos recibir a túa axuda para crear o mellor xogo de programación da historia." class_attributes: "Atributos das Clases" archmage_attribute_1_pref: "Coñecemento en " - archmage_attribute_1_suf: ", ou desexo por aprender. A maior parte do noso código está escrito nesta linguaxe. Si eres un fan de Ruby ou Python sentiraste como na casa. É JavaScript pero cunha sintaxe máis agradable." - archmage_attribute_2: "Algunha experiencia en programación e iniciativa personal. Orientaremoste, pero non podemos pasar moito tempo ensinándote." - how_to_join: "Cómo unirse" + archmage_attribute_1_suf: ", ou desexo por aprender. A maior parte do noso código está escrito nesta linguaxe. Se eres un fan de Ruby ou Python sentiraste como na casa: é como JavaScript, pero cunha sintaxe máis agradable." + archmage_attribute_2: "Algunha experiencia en programación e iniciativa personal. Orientarémoste, pero non podemos pasar moito tempo ensinándote." + how_to_join: "Como unirse" join_desc_1: "Calquera pode axudar! So bótalle un ollo ao noso " join_desc_2: "para comenzar e marca a casilla de abaixo para etiquetarte como un bravo Archimago e obter as últimas novas por correo electrónico. Queres falar sobre que facer ou como involucrarte máis? " join_desc_3: ", ou atópanos no noso " join_desc_4: "e partiremos dende ese punto!" join_url_email: "Escríbenos un correo electrónico" join_url_hipchat: "sala pública en HipChat" - more_about_archmage: "Aprende máis sobre convertirte nun poderoso Archimago" archmage_subscribe_desc: "Recibe correos sobre novos anuncios e oportunidades de codificar." - artisan_summary_pref: "Queres deseñar niveis e aumentar o arsenal de CodeCombat? A xente está xogando co noso contido a un ritmo máis rápido do que podemos construir! Neste momento, o nostro editor de niveis está nunha fase temperá, así que ten coidado. Facer niveis será un pouco complicado e haberá erros. Si tes en mente campañas fantásticas que o abrangan todo" - artisan_summary_suf: ", entón esta Clase é a túa." - artisan_introduction_pref: "Debemos construir niveis adicionais! A xente pide máis contidos e so podemos crear uns cantos. Agora mesmo a túa estación de traballo é o nivel un; o noso editor de niveis apenas é utilizable polos seus creadores, así que ten coidado. Si tes visións de campañas que acadan o infinito" + artisan_introduction_pref: "Debemos construir niveis adicionais! A xente pide máis contidos e só podemos crear uns cantos. Agora mesmo a túa estación de traballo é o nivel un; o noso editor de niveis apenas é utilizable polos seus creadores, así que ten coidado. Se tes visións de campañas que acadan o infinito" artisan_introduction_suf: ", entón esta Clase é ideal para ti." artisan_attribute_1: "Calquera experiencia creando contido semellante estaría ben, como por exemplo o editor de niveis de Blizzard. Ainda que non se precisa!" - artisan_attribute_2: "Un desexo de facer unha morea de testeo e iteracións. Para facer bos niveis necesitas ensinarllos a outros e ver como xogan, ademáis de estar preparado para atopar os fallos a arranxar." + artisan_attribute_2: "Un desexo de facer unha morea de testeo e iteracións. Para facer bos niveis necesitas ensinarllos a outros e ver como xogan, ademáis de estar preparado para atopar os fallos a arranxalos." artisan_attribute_3: "Polo momento, a resistencia vai á par co Aventureiro. O noso editor de niveis está a un nivel de desenvolvemento temperá e pode ser moi frustrante usalo. Estás advertido!" artisan_join_desc: "Segue as seguintes indicacións para usar o editor de niveis. Tómao ou déixao:" artisan_join_step1: "Le a documentación." artisan_join_step2: "Crea un novo nivel e explora os niveis existentes." artisan_join_step3: "Busca a nosa sala pública de HipChat en busca de axuda." artisan_join_step4: "Publica os teus niveis no foro para recibir comentarios críticos." - more_about_artisan: "Aprende máis sobre convertirte nun Artesán creativo" artisan_subscribe_desc: "Recibe correos sobre actualizacións do editor de niveis e anuncios." - adventurer_summary: "Permite que sexamos claros sobre o teu papel: ti eres o tanque. Vas a recibir fortes danos. Precisamos xente que probe os niveis máis novos e axude a identificar como mellorar. A dor vai a ser enorme; facer bos xogos é un proceso longo e ninguén o fai ben a primeira vez. Si podes sobrevivir e obter unha puntuación alta en resistencia, entón esta clase é para ti." - adventurer_introduction: "Falemos claro sobre o teu papel: ti eres o tanque. Vas a recibir fortes danos. Precisamos xente que probe os niveis máis novos e axude a identificar como mellorar. A dor vai a ser enorme; facer bos xogos é un proceso longo e ninguén o fai ben a primeira vez. Si podes sobrevivir e obter unha puntuación alta en resistencia, entón esta clase é para ti." - adventurer_attribute_1: "Estar sedento de coñeceentos. Queres aprender a programar e nos queremos ensinarche como facelo. Ainda qe neste caso é máis probable que sexas ti o que esté facendo a maior parte do ensino." - adventurer_attribute_2: "Carismático. Se amable pero claro á hora de desglosar qué precisa ser mellorado e suxire de que formas podería facerse." - adventurer_join_pref: "Reúnete cun (ou recluta!) Artesán e traballa con eles, ou marca a casilla de abaixo para recibir un correo cando haxa novos niveis para probar. También publicaremos nas nosas redes novos niveis para revisar" + adventurer_introduction: "Falemos claro sobre o teu papel: ti eres o tanque. Vas a recibir fortes danos. Precisamos xente que probe os niveis máis novos e axude a identificar como mellorar. A dor vai a ser enorme; facer bós xogos é un proceso longo e ninguén o fai ben a primeira vez. Se podes sobrevivir e obter unha puntuación alta en resistencia, entón esta clase é para ti." + adventurer_attribute_1: "Estar sedento de coñecementos. Queres aprender a programar e nós queremos ensinarte como facelo. Ainda que neste caso é máis probable que sexas ti o que esté facendo a maior parte do ensino." + adventurer_attribute_2: "Carismático. Non esquezas ser amable pero claro á hora de desglosar que precisa ser mellorado e suxerir de que formas podería facerse." + adventurer_join_pref: "Reúnete cun (ou recluta!) Artesán e traballa con eles, ou marca a casilla de abaixo para recibir un correo cando haxa novos niveis para probar. Tamén publicaremos nas nosas redes novos niveis para revisar." adventurer_forum_url: "o noso foro" - adventurer_join_suf: "así que si prefieres estar informado nesa forma, crea unha conta alí!" - more_about_adventurer: "Aprende máis sobre cómo convertirte nun bravo Aventureiro" + adventurer_join_suf: "así que se prefires estar informado nesa forma, crea unha conta alí!" adventurer_subscribe_desc: "Recibe correos cando haxa novos niveis para probar." - scribe_summary_pref: "CodeCombat non vai a ser so un conxunto de niveis. Tamén será una fonte de coñecemento sobre programación á que os xogadores poderán recurrir. De esa maneira, cada Artesán pode ligar a un artigo detallado que axude ao xogador: documentación afín ao que el " - scribe_summary_suf: " escribiu. Si che gusta explicar conceptos de programación, entón esta clase é para tí." - scribe_introduction_pref: "CodeCombat non será so unha morea de niveis. Tamén será una fonte de coñecementos, unha wiki de conceptos de programación á que os niveis se engancharán. Desa forma, en lugar de que cada Artesán teña que describir en detalle que é un operador de comparación, poderá sinxelamente ligar o nivel ao Artigo que os describe e que xa foi escrito para preparación do jugador. Algo na liña do que a " + scribe_introduction_pref: "CodeCombat non será só unha morea de niveis. Tamén será una fonte de coñecementos, unha wiki de conceptos de programación á que os niveis se engancharán. Desa forma, en lugar de que cada Artesán teña que describir en detalle que é un operador de comparación, poderá sinxelamente ligar o nivel ao Artigo que os describe e que xa foi escrito para a preparación do xogador. Algo na liña do que a " scribe_introduction_url_mozilla: "Mozilla Developer Network" - scribe_introduction_suf: " construiu. Si o que che gusta é articular os conceptos da programación dunha forma sinxela, entón esta clase é para ti." - scribe_attribute_1: "Habilidade á hora de escribir é casi todo o que precisas. Non so dominar a gramática e a ortografía sinon tamén expresar ideas complicadas aos demais de forma sinxela." - contact_us_url: "Escribenos un correo electrónico" - scribe_join_description: "contanos máis sobre ti, a túa experiencia no mundo da programación e sobre que cousas che gustaría escribir. E continuaremos a partir de ahí!" - more_about_scribe: "Aprende más sobre convertirte en un Escriba diligente" + scribe_introduction_suf: " construiu. Se o que te gusta é articular os conceptos de programación dunha forma sinxela, entón esta clase é para ti." + scribe_attribute_1: "Habilidade á hora de escribir é case todo o que precisas. Non só dominar a gramática e a ortografía senón tamén expresar ideas complicadas aos demais de forma sinxela." + contact_us_url: "Escríbenos un correo electrónico" + scribe_join_description: "contanos máis sobre ti, a túa experiencia no mundo da programación e sobre que cousas te gustaría escribir. E continuaremos a partir de ahí!" scribe_subscribe_desc: "Recibe correos sobre anuncios de redacción de Artigos." - diplomat_summary: "Hai un gran interese por CodeCombat noutros países que non falan inglés! Estamos buscando traductores que estén dispostos a pasar o seu valioso tempo traduciendo o corpus de palabras do sitio web para que CodeCombat sexa accesible a todo o mundo tan pronto como sexa posible. Si desexas axudar para facer de CodeCombat algo internacional, entón esta clase é para tí." - diplomat_introduction_pref: "Así, si hemos aprendido algo desde el " - diplomat_launch_url: "lanzamiento en octubre" - diplomat_introduction_suf: "hai un interés considerable en CodeCombat en otros paises, ¡especialmente Brasil! Estamos formando un cuerpo de traductores con ganas de traducir un grupo de palabras tras otro para hacer CodeCombat tan accesible para todo el mundo como sea posible. Si quieres recibir avances de próximos contenidos y quieres poner esos niveles a disposición de los que comparten tu idioma tan pronto como sea posible, entonces esta Clase es para ti." - diplomat_attribute_1: "Fluidez co inglés e a linguaxe á que queiras traducir. Cando se trata de transmitir ideas complexas, és importante ter grandes coñecementos das dúas!" + diplomat_introduction_pref: "Así, se aprendimos algo dende o " + diplomat_launch_url: "lanzamento en outubro" + diplomat_introduction_suf: "hai un interés considerable en CodeCombat en outros paises, ¡especialmente no Brasil! Estamos formando un corpo de tradutores con ganas de traducir un grupo de palabras tras outro para facer CodeCombat tan accesible para todo o mundo como sexa posible. Se queres recibir avances dos próximos contdos e se queeres poñer esos niveis á disposición dos que comparten o teu idioma tan pronto como sexa posible, entón esta Clase é para ti." + diplomat_attribute_1: "Fluidez co inglés e a lingua á que queiras traducir. Cando se trata de transmitir ideas complexas, é importante ter grandes coñecementos das dúas!" # diplomat_i18n_page_prefix: "You can start translating our levels by going to our" # diplomat_i18n_page: "translations page" # diplomat_i18n_page_suffix: ", or our interface and website on GitHub." diplomat_join_pref_github: "Atopa o ficheiro local do teu idioma " diplomat_github_url: "en GitHub" - diplomat_join_suf_github: ", edítao en liña, e solicita que sexa revisado. Ademais, marca a casilla de abaixo para mantenerte informado en novos progresos en Internacionalización." - more_about_diplomat: "Aprende máis sobre como convertirte nun gran Diplomático" + diplomat_join_suf_github: ", edítao en liña, e solicita que sexa revisado. Ademais, marca a casilla de abaixo para manterte informado dos novos progresos en Internacionalización." diplomat_subscribe_desc: "Recibe correos sobre novos niveis e desenvolvementos para traducir." - ambassador_summary: "Estamos tratando de construir unha comunidade, e cada comunidade precisa un equipo de apoio para cando hai problemas. Temos chats, correos electrónicos e redes sociais para que os nosos usuarios poidan familiarizarse co xogo. Si queres axudar a que a xente participe, se divierta e aprenda algo de programación, entón esta clase é para tí." - ambassador_introduction: "Esta é unha comunidade en construcción e ti eres parte das conexións. Temos chat Olark, correos electrónicos e as redes sociais con unha gran cantidade de persoas con quen falar, axudar a familiarizarse co xogo e aprender. Si queres axudar á xente a que se involucre, se divirta e teña boas sensacións sobre CodeCombat e cara onde vamos, entón esta clase é para ti." - ambassador_attribute_1: "Habilidades de comunicación. Ser capaz de identificar os problemas que os xogadores están tendo e axudarlles a resolvelos. Ademais, manter ao resto de nos informados sobre o que están dicindo os xogadores, o que lles gusta, o que non, e do que queren máis!" + ambassador_introduction: "Esta é unha comunidade en construcción e ti es parte das conexións. Temos chat Olark, correos electrónicos e as redes sociais cunha gran cantidade de persoas con quen falar, axudar a familiarizarse co xogo e aprender. Se queres axudar á xente a que se involucre, se divirta e teña boas sensacións sobre CodeCombat e cara onde vamos, entón esta clase é para ti." + ambassador_attribute_1: "Habilidades de comunicación. Ser capaz de identificar os problemas que os xogadores están tendo e axudarlles a resolvelos. Ademais, manter ao resto de nos informados sobre o que están dicindo os xogadores, o que lles gusta, o que non, e do que máis queren!" ambassador_join_desc: "contanos máis sobre ti, que fixeches e que estarías interesado en facer. E continuaremos a partir de ahí!" ambassador_join_note_strong: "Nota" - ambassador_join_note_desc: "Una das nosas principais prioridades é construir un modo multixogador onde os xogadores con maiores dificultades á hora de resolver un nivel, poidan invocar aos magos máis avanzados para que lles ayuden. Será unha boa maneira de que os Embaixadores poidan facer o seu traballo. Mantenremoste informado!" - more_about_ambassador: "Aprende máis sobre como convertirte nun amable Embaixador" + ambassador_join_note_desc: "Una das nosas principais prioridades é construir un modo multixogador onde os xogadores con maiores dificultades á hora de resolver un nivel, poidan invocar aos magos máis avanzados para que lles axuden. Será unha boa maneira de que os Embaixadores poidan facer o seu traballo. Manterémoste informado!" ambassador_subscribe_desc: "Recibe correos sobre actualizacións de soporte e desenvolvemento do multixogador." changes_auto_save: "Os cambios son gardados automáticamente cando marcas as casillas de verificación." diligent_scribes: "Os nosos dilixentes Escribas:" powerful_archmages: "Os nosos poderosos Archimagos:" creative_artisans: "Os nosos creativos Artesáns:" - brave_adventurers: "Os nosos bravus Aventureiros:" + brave_adventurers: "Os nosos bravos Aventureiros:" translating_diplomats: "Os nosos políglotas Diplomáticos:" helpful_ambassadors: "Os nosos amables Embaixadores:" ladder: - please_login: "Por favor inicia sesión antes de xogar unha partida para o escalafón." + please_login: "Por favor, inicia sesión antes de xogar unha partida para o escalafón." my_matches: "As miñas partidas" simulate: "Simular" simulation_explanation: "Simulando partidas podes facer que a túa partida sexa calificada máis rápido!" @@ -709,7 +1013,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr ratio: "Ratio" leaderboard: "Clasificación" battle_as: "Pelexa como " - summary_your: "As Túas " + summary_your: "As túas " summary_matches: "Partidas - " summary_wins: " Vitorias, " summary_losses: " Derrotas" @@ -724,15 +1028,15 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr code_being_simulated: "O teu novo código está sendo simulado por outros xogadores para ser calificado. Irase actualizando a medida que as partidas se vaian sucedendo." no_ranked_matches_pre: "Non hai partidas calificadas para " no_ranked_matches_post: " equipo! Xoga contra outros competidores e logo volta aquí para que a túa partida apareza na clasificación." - choose_opponent: "Elixe un contrincante" - select_your_language: "Elixe o teu Idioma!" + choose_opponent: "Escolle un contrincante" + select_your_language: "Escolle o teu idioma!" tutorial_play: "Xogar o Tutorial" - tutorial_recommended: "Recomendado si non xogaches antes." + tutorial_recommended: "Recomendado se non xogaches antes." tutorial_skip: "Saltar o Tutorial" tutorial_not_sure: "Non estás seguro de como funciona esto?" tutorial_play_first: "Proba o Tutorial primeiro." simple_ai: "IA sinxela" - warmup: "calentamento" + warmup: "quentamento" friends_playing: "Amigos xogando" log_in_for_friends: "Inicia sesión para xogar cos teus amigos!" social_connect_blurb: "Conéctate e xoga contra os teus amigos!" @@ -740,12 +1044,14 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr fight: "Pelexa!" watch_victory: "Ver a túa vitoria" defeat_the: "Gaña a" +# tournament_started: ", started" tournament_ends: "O torneo remata" tournament_ended: "O torneo rematou" tournament_rules: "Regras do Torneo" - tournament_blurb: "Escribe código, colleira ouro, constrúe exércitos, aplasta aos malos, gaña premios e sube na túa carreira no noso Torneo da Avaricia con $40,000! Mira os detalles" + tournament_blurb: "Escribe código, consigue ouro, constrúe exércitos, aplasta aos malos, gaña premios e sube na túa carreira no noso Torneo da Avaricia con $40,000! Mira os detalles" tournament_blurb_criss_cross: "Gaña puxas, constrúe camiños, aniquila aos teus opoñentes, recolle xemas, e mellora a túa carreira no noso torneo Criss-Cross! Mira os detalles" - tournament_blurb_blog: "no noso blogue" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" + tournament_blurb_blog: "no noso blog" rules: "Regras" winners: "Gañadores" @@ -755,14 +1061,15 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr multiplayer_title: "Niveis Multixogador" achievements_title: "Logros" last_played: "Último Xogado" - status: "Estatus" + status: "Status" status_completed: "Completado" - status_unfinished: "Sin Rematar" + status_unfinished: "Sen Rematar" no_singleplayer: "Ainda non xogaches ningún nivel individual." no_multiplayer: "Ainda non xogaches ningún nivel multixogador." no_achievements: "Ainda non alcanzaches ningún logro." favorite_prefix: "O teu idioma favorito " favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." achievements: last_earned: "Gañado a última vez" @@ -773,7 +1080,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr # category_level: "Level" category_miscellaneous: "Miscelánea" category_levels: "Niveis" - category_undefined: "Sen categorizar" + category_undefined: "Sen categoría" current_xp_prefix: "" current_xp_postfix: " en total" new_xp_prefix: "" @@ -784,7 +1091,36 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr account: recently_played: "Xogado Recientemente" - no_recent_games: "Non xogeis xogos nas últimas dúas semáns." + no_recent_games: "Non xogaches xogos nas últimas dúas semánas." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "Erro ao cargar dende o servidor." @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr leaderboard: "Clasificación" user_schema: "Esquema de usuario" user_profile: "Perfil de usuario" +# patch: "Patch" patches: "Parches" patched_model: "Documento Fonte" model: "Modelo" @@ -838,18 +1175,45 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr # user_remarks: "User Remarks" versions: "Versións" items: "Obxectos" +# hero: "Hero" heroes: "Heroes" - wizard: "Mago" achievement: "Logro" clas: "Clases" play_counts: "Contador de Xogos" feedback: "Apoio" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" delta: added: "Engadido" modified: "Modificado" +# not_modified: "Not Modified" deleted: "Eliminado" - moved_index: "Índice Movido" + moved_index: "Índice movido" # text_diff: "Text Diff" # merge_conflict_with: "MERGE CONFLICT WITH" no_changes: "Sen Cambios" @@ -864,40 +1228,34 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr multiplayer_link_description: "Pasa este enlace a alguén para que se una a ti." multiplayer_hint_label: "Pista:" multiplayer_hint: " Preme na ligazón para que se seleccione, despois utiliza Ctrl-C ou ⌘-C para copiar a ligazón." - multiplayer_coming_soon: "Máis opcións de Multixogador están por vir!" + multiplayer_coming_soon: "Pronto teremos máis opcións de Multixogador!" multiplayer_sign_in_leaderboard: "Accede á túa conta ou crea unha conta para gardar os teus resultados na táboa de clasificación." legal: page_title: "Legal" - opensource_intro: "CodeCombat é de balde e totalmente código aberto." + opensource_intro: "CodeCombat é de balde e totalmente con código aberto." opensource_description_prefix: "Bota un ollo a " github_url: "o noso GitHub" - opensource_description_center: "e axúdanos si queres. CodeCombat está desenvolvido sobre ducias de proxectos de código aberto, e estamos encantados. Mira " + opensource_description_center: "e axúdanos se queres. CodeCombat está desenvolvido sobre ducias de proxectos de código aberto, e estamos encantados. Mira " archmage_wiki_url: "a nosa wiki do Archimago" opensource_description_suffix: "para atopar unha lista do software que fai posible este xogo." practices_title: "Prácticas respetuosas" - practices_description: "Esto é o que che propoñemos a ti, o xogador, sin usar moita terminoloxía legal." + practices_description: "Isto é o que che propoñemos a ti, o xogador, sen usar moita terminoloxía legal." privacy_title: "Privacidade" - privacy_description: "Non venderemos a túa información persoal. Temos a intención de facer diñeiro a través da contratación co tempo, pero podes estar seguro que non vamos a distribuir a túa información persoal ás empresas interesadas sin o teu consentimento expreso." +# privacy_description: "We will not sell any of your personal information." security_title: "Seguridade" - security_description: "Esforzamonos por manter segura a túa información persoal. Como proxecto de código aberto, o noso sitio está aberto a calqueira que queira revisalo e mellorar nosos sistemas de seguridade." + security_description: "Esforzámonos por manter segura a túa información persoal. Como proxecto de código aberto, o noso sitio está aberto a calqueira que queira revisalo e mellorar os nosos sistemas de seguridade." email_title: "Correo electrónico" email_description_prefix: "Non te inundaremos con spam. Mediante" email_settings_url: "os teus axustes de correo electrónico" - email_description_suffix: "ou a través das ligazóns nos correos que che enviemos, podes cambiar as túas preferencias e darte de baixa de xeito doado en calquera momento." + email_description_suffix: "ou a través das ligazóns nos correos que te enviemos, podes cambiar as túas preferencias e darte de baixa doadamente en calquera momento." cost_title: "Prezo" - cost_description: "Actualmente, CodeCombat é 100% de balde! Un dos nosos principais obxectivos é mantenlo así, de forma que o maior número posible de xente poida xogar, independientemente das súass posibilidades económicas. Si as cousas se torcen, quizais teñamos que cobrar suscripcións por algún contido, pero preferimos non facelo. Cun pouco de sorte, poderemos manter a empresa con: " - recruitment_title: "Contratación" - recruitment_description_prefix: "En CodeCombat, vaste convertir nun poderoso mago non solo no xogo, tamén no mundo real." - url_hire_programmers: "Ninguén pode contratar programadores coa suficiente rapidez" - recruitment_description_suffix: "así que unha vez que teñas afiado as túas habilidades e si estás de acordo, mostraremos os teus mellores logros en programación ás miles de empresas que estén desexando ter a oportunidade de contratarte. Eles pagannos un pouco e paganche a ti" - recruitment_description_italic: "moito." - recruitment_description_ending: "A web permanece de balde e todo o mundo é feliz. Ese é o plan." + cost_description: "Actualmente, CodeCombat é 100% de balde! Un dos nosos principais obxectivos é mantelo así, de forma que o maior número posible de xente poida xogar, independientemente das súas posibilidades económicas. Se as cousas van mal, se cadra teremos que cobrar suscripcións por algún contido, pero preferimos non facelo. Cun pouco de sorte, poderemos manter a empresa con: " copyrights_title: "Copyrights e Licenzas" contributor_title: "Acordo de Licenza do Colaborador" contributor_description_prefix: "Todas as colaboracións, tanto na web como no noso repositorio de GitHub, están suxeitas ao noso" cla_url: "CLA" - contributor_description_suffix: "con que deberás estar de acordo antes de colaborar." + contributor_description_suffix: "co que deberás estar de acordo antes de colaborar." code_title: "Código - MIT" code_description_prefix: "Todo o código propiedade de CodeCombat ou aloxado en codecombat.com, ámbolos dous no repositorio GitHub ou na base de datos de codecombat.com, está licenciado baixo a " mit_license_url: "Licenza MIT" @@ -911,26 +1269,26 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr art_artwork: "Arte" art_sprites: "Persoaxes" art_other: "Outros traballos creativos non relacionados con código postos a disposición para a creación de Niveis." - art_access: "Actualmente non hai un sistema universal e doado para ir en busca deses recursos. En xeral, recóllelos das URLs como as usadas no sitio, contáctanos para recibir asistencia, ou axúdanos a extender o sitio para facer accesibles estes recursos de maneira máis doada." - art_paragraph_1: "Para a atribución, por favor pon o teu nome e liga a codecombat.com perto do lugar onde se utiliza a fonte ou no seu caso para o medio. Por exemplo:" - use_list_1: "Si se usa nunha película ou outro xogo, inclúe codecombat.com nos créditos." - use_list_2: "Si se usa nunha páxina web, inclúe unha ligazón perto de onde se use, por exemplo baixo unha imaxe, ou nunha páxina xeral de atribucións onde tamén menciones outros traballos Creative Commons e software de código aberto que uses na túa web. Si xa se fai clara referencia a CodeCombat, como no post de un blogue mencionando a CodeCombat, non é precisa unha atribución do contido por separado." - art_paragraph_2: "Si o contido usado foi creado non por CodeCombat sinón por un usuario de codecombat.com, deberá serlle atribuido a dito usuario e seguir as directrices de atribución proporcionadas na descripción do recurso, si as hai." + art_access: "Actualmente non hai un sistema universal e doado para ir en busca destes recursos. En xeral, recóllelos das URLs como as usadas no sitio, contáctanos para recibir asistencia, ou axúdanos a estender o sitio para facer accesibles estes recursos de maneira máis fácil." + art_paragraph_1: "Para a atribución, por favor pon o teu nome e liga a codecombat.com preto do lugar onde se utiliza a fonte ou no seu caso para o medio. Por exemplo:" + use_list_1: "Se se usa nunha película ou outro xogo, inclúe codecombat.com nos créditos." + use_list_2: "Se se usa nunha páxina web, inclúe unha ligazón preto de onde se use, por exemplo baixo unha imaxe, ou nunha páxina xeral de atribucións onde tamén menciones outros traballos Creative Commons e software de código aberto que uses na túa web. Se xa se fai clara referencia a CodeCombat, como no post de un blog mencionando a CodeCombat, non é precisa unha atribución do contido por separado." + art_paragraph_2: "Se o contido usado foi creado non por CodeCombat senón por un usuario de codecombat.com, deberá serlle atribuido a dito usuario e seguir as directrices de atribución proporcionadas na descripción do recurso, se as hai." rights_title: "Dereitos Reservados" rights_desc: "Todos os dereitos reservados para os Niveis. Esto inclúe" rights_scripts: "Scripts" rights_unit: "Configuración da Unidade" rights_description: "Descripción" rights_writings: "Escritos" - rights_media: "Elementos media (sons, música) e calqueira outro contido creativo creado específicamente para ese Nivel e que non estivera dispoñible para todos ao crear o/os niveis." + rights_media: "Elementos audiovisuais (sons, música) e calqueira outro contido creativo creado específicamente para ese Nivel e que non estivera dispoñible para todos ao crear o/os niveis." rights_clarification: "Para aclarar, calquera cousa que se pon a disposición no editor de niveis co fin de crear Niveis atópase baixo licenza CC, mentras que o contido creado co editor de niveis ou subido no curso da creación de niveis non o é." nutshell_title: "Nunha palabra" - nutshell_description: "Todos os recursos que ofrecemos no editor de niveis son libres de ser utilizados para crear niveis. Pero reservámosnos o dereito de restrinxir a distribución dos propios niveis (que se crean en codecombat.com) de modo que se poida cobrar por eles no futuro, si eso é o que termina sucedendo." - canonical: "A versión inglesa deste documento é a canónica, a definitiva. Si hai algunha diferenza coa que poida aparecer nas traduccións, a versión inglesa é a que prevalece sobre as demais." + nutshell_description: "Todos os recursos que ofrecemos no editor de niveis son libres de ser utilizados para crear niveis. Pero reservámosnos o dereito de restrinxir a distribución dos propios niveis (que se crean en codecombat.com) de modo que se poida cobrar por eles no futuro, se iso é o que termina por suceder." + canonical: "A versión inglesa deste documento é a canónica, a definitiva. Se hai algunha diferenza coa que poida aparecer nas traduccións, a versión inglesa é a que prevalece sobre as demais." ladder_prizes: title: "Premios do Torneo" # This section was for an old tournament and doesn't need new translations now. - blurb_1: "Estes premios entregaranse acorde a" + blurb_1: "Estes premios entregaranse dacordo " blurb_2: "as regras do torneo" blurb_3: "Aos primeiros xogadores humanos e ogros." blurb_4: "Dous equipos significa doble-premio!" @@ -940,7 +1298,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr total_value: "Valor Total" in_cash: "en diñeiro" custom_wizard: "Persoaliza o teu Mago de CodeCombat" - custom_avatar: "Persoaliza o teu avatar de CoceCombat" + custom_avatar: "Persoaliza o teu avatar de CodeCombat" heap: "Por seis meses de acceso \"Startup\"" credits: "créditos" one_month_coupon: "cupón: elixe entre Rails ou HTML" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr license: "licenza" oreilly: "ebook da túa escolla" - wizard_settings: - title: "Axustes do mago" - customize_avatar: "Persoaliza o teu Avatar" - active: "Activo" - color: "Cor" - group: "Grupo" - clothes: "Roupa" - trim: "Decoración" - cloud: "Nube" - team: "Equipo" - spell: "Feitizo" - boots: "Botas" - hue: "Matiz" - saturation: "Saturación" - lightness: "Brilo" - account_profile: settings: "Configuración" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Editar Perfil" @@ -977,10 +1319,10 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr contact: "Contacto" active: "Buscando entrevistas de traballo" inactive: "Non busco entrevistas de traballo agora mesmo" - complete: "completado" + complete: "Completado" next: "Seguinte" next_city: "Cidade?" - next_country: "elixe o teu país." + next_country: "escolle o teu país." next_name: "Nome?" next_short_description: "escribe unha descripción breve." next_long_description: "describe o teu posto de traballo desexado." @@ -988,13 +1330,13 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr next_work: "Resume a túa historia laboral." # next_education: "recount your educational ordeals." next_projects: "Amósanos tres proxectos nos que teñas traballado." - next_links: "engade ligazóns personais ou de redes sociais." - next_photo: "engadir unha foto para o perfil profesional." - next_active: "amosarte aberto a ofertas nas búsquedas." - example_blog: "Blogue" + next_links: "Engade ligazóns personais ou de redes sociais." + next_photo: "Engadir unha foto para o perfil profesional." + next_active: "Amosarte aberto a ofertas nas búsquedas." + example_blog: "Blog" example_personal_site: "Web personal" links_header: "Ligazóns Personais" - links_blurb: "Liga a outros sitios ou perfiles que queiras destacar como o teu GitHub, LinkedIn, ou o teu blogue." + links_blurb: "Liga a outros sitios ou perfiles que queiras destacar como o teu GitHub, LinkedIn, ou o teu blog." links_name: "Nome da ligazón" links_name_help: "Que estás ligando?" links_link_blurb: "URL da ligazón" @@ -1006,16 +1348,16 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr basics_city: "Cidade" basics_city_help: "Cidade na que queres traballar (ou na que vives actualmente)." basics_country: "País" - basics_country_help: "País no que queeres traballar (ou no que vives actualmente)." - basics_visa: "Estatus de Traballo en EEUU" - basics_visa_help: "Estas autorizado a traballar en EEUU, ou precisas unha visa patrocinada? (Si vives en Canada ou Australia, marca autorizado.)" + basics_country_help: "País no que queres traballar (ou no que vives actualmente)." + basics_visa: "Status de Traballo en EEUU" + basics_visa_help: "Estás autorizado a traballar en EEUU, ou precisas unha visa patrocinada? (Se vives en Canadá ou Australia, marca autorizado.)" basics_looking_for: "Estou Buscando" basics_looking_for_full_time: "Full-time (tempo completo)" basics_looking_for_part_time: "Tempo Parcial" basics_looking_for_remote: "Dende a Casa" basics_looking_for_contracting: "Contractual" basics_looking_for_internship: "Interino" - basics_looking_for_help: "Que tipo de posto de desenvolemento che gustaría?" + basics_looking_for_help: "Que tipo de posto de desenvolemento te gustaría?" name_header: "Escribe o teu nome" name_anonymous: "Desenvolvedor Anónimo" name_help: "Esto é o que queres que vexan as empresas, algo como 'Nick Winter'." @@ -1025,14 +1367,14 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr short_description_help: "Quen es, e que estás buscando? 140 caracteres como máximo." skills_header: "Habilidades" # skills_help: "Tag relevant developer skills in order of proficiency." - long_description_header: "Describenos o teu traballo soñado" + long_description_header: "Descríbenos o teu traballo soñado" long_description_blurb: "Conta aos teus empregadores o impresionante que es e que traballo estás buscando." long_description: "Descripción Persoal" # long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." work_experience: "Experiencia laboral" - work_header: "Contanos un pouco da túa historial laboral" + work_header: "Cóntanos un pouco a túa historial laboral" work_years: "Anos de experiencia" - work_years_help: "Cantos anos de experiencia profesional(remunerada) como desenvolvedor de sofware tes?" + work_years_help: "Cantos anos tes de experiencia profesional (remunerada) como desenvolvedor de sofware?" work_blurb: "Fai un listado coa experiencia laboral relevante, o máis recente primeiro." work_employer: "Empresa/Empregador" work_employer_help: "Nome da empresa / empregador." @@ -1052,7 +1394,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr education_duration: "Duración" education_duration_help: "Cando?" education_description: "Descripción" - education_description_help: "Resalta algo de da túa experiencia educativa. (140 caracteres maximo; opcional)" + education_description_help: "Resalta algo da túa experiencia educativa. (140 caracteres maximo; opcional)" our_notes: "As nosas notas" # remarks: "Remarks" projects: "Proxectos" @@ -1064,7 +1406,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr project_description: "Descripción" project_description_help: "Describe o proxecto brevemente." project_picture: "Foto" - project_picture_help: "Sube unha imaxe de 230x115px ou máis grande ensinándonos o proxecto." + project_picture_help: "Sube unha imaxe de 230x115 px ou máis grande ensinándonos o proxecto." project_link: "Ligazón" project_link_help: "Ligazón ao proxecto." player_code: "Código de Xogador" @@ -1072,8 +1414,8 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr employers: # deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." # deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." - hire_developers_not_credentials: "Contrata desenvolvedores, no credenciales." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. - get_started: "Empezar" + hire_developers_not_credentials: "Contrata desenvolvedores, non credenciais." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. + get_started: "Comezar" # already_screened: "We've already technically screened all our candidates" filter_further: ", Pero podes filtrar máis alá:" filter_visa: "Visa" @@ -1083,7 +1425,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr filter_education_other: "Outros" filter_role_web_developer: "Desenvolvedor Web" filter_role_software_developer: "Desenvolvedor de Software" - filter_role_mobile_developer: "Desenvolvedor de Mobiles" + filter_role_mobile_developer: "Desenvolvedor de Móbiles" filter_experience: "Experiencia" filter_experience_senior: "Senior" filter_experience_junior: "Junior" @@ -1094,8 +1436,8 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr reasons: "Tres razóns polas que deberías contratar a través de nos:" everyone_looking: "Todo o mundo aquí está buscando a súa proxima oportunidade." # everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." - weeding: "Séntate; nos fixemos o traballo por ti." - weeding_blurb: "Todos os xogadores que poñemos na lista foron seguidos polas súas habilidades técnicas. Tamén facemos chamadas de teléfono para os candidatos seleccionados e anotamos nos seus perfiles para aforrar tempo." + weeding: "Séntate; nós fixemos o traballo por ti." + weeding_blurb: "Todos os xogadores que poñemos na lista foron seguidos polas súas habilidades técnicas. Tamén facemos chamadas de teléfono para os candidatos seleccionados e anotamos nos seus perfís para aforrar tempo." # pass_screen: "They will pass your technical screen." # pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." make_hiring_easier: "Facer a miña contratación máis doada, por favor." @@ -1108,11 +1450,11 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr candidate_looking_for: "Buscando" candidate_role: "Rol" candidate_top_skills: "Mellores Habilidades" - candidate_years_experience: "Anos Exp" + candidate_years_experience: "Anos Experiencia" candidate_last_updated: "Última actualización" candidate_who: "Quen" featured_developers: "Desenvolvedores Destacados" - other_developers: "Otros Desenvolvedores" + other_developers: "Outros Desenvolvedores" inactive_developers: "Desenvolvedores Inactivos" admin: @@ -1126,7 +1468,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr av_entities_users_url: "Usuarios" av_entities_active_instances_url: "Instancias Activas" av_entities_employer_list_url: "Lista de empregados" - av_entities_candidates_list_url: "Lista de Candidatos" + av_entities_candidates_list_url: "Lista de candidatos" av_entities_user_code_problems_list_url: "Lista de Problemas de Código do Usuario" av_other_sub_title: "Outros" av_other_debug_base_url: "Base (para debugging base.jade)" diff --git a/app/locale/he.coffee b/app/locale/he.coffee index 20d669e2b..532886f47 100644 --- a/app/locale/he.coffee +++ b/app/locale/he.coffee @@ -3,25 +3,26 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", slogan: "גם לשחק וגם ללמוד לתכנת" no_ie: "המשחק לא עובד באקפלורר 8 וישן יותר. סליחה!" # Warning that only shows up in IE8 and older no_mobile: "המשחק לא עוצב לטלפונים ואולי לא יעבוד" # Warning that shows up on mobile devices - play: "שחק" # The big play button that just starts playing a level + play: "שחק" # The big play button that opens up the campaign view. old_browser: "או או, נראה כי הדפדפן שלך יותר מידי ישן כדי להריץ את המשחק. סליחה!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "אתה יכול לנסות בכול מקרה אבל זה כנראה לא יעבוד." + ipad_browser: "חדשות רעות: CodeCombat לא עובד בדפדפן של הiPad-. חדשות טובות: גרסת הiPad של המשחק ממתינה לאישור מחברת Apple." campaign: "מסע" for_beginners: "למתחילים" multiplayer: "רב-משתתפים" # Not currently shown on home page - for_developers: "למומחים" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + for_developers: "למפתחים" # Not currently shown on home page. + or_ipad: "iPadאו הורד ל" nav: play: "שלבים" # The top nav bar entry where players choose which levels to play -# community: "Community" + community: "קהילה" editor: "עורך" blog: "בלוג" forum: "פורום" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + account: "חשבון" + profile: "פרופיל" + stats: "נתונים" + code: "קוד" admin: "אדמין" # Only shows up when you are an admin home: "בית" contribute: "תרום" @@ -29,7 +30,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", about: "עלינו" contact: "צור קשר" twitter_follow: "עקוב אחרינו בטוויטר" -# teachers: "Teachers" + teachers: "מורים" modal: close: "סגור" @@ -40,8 +41,8 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", diplomat_suggestion: title: "עזור לתרגם את CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. - sub_heading: "אנו צריכים את קישורי השפה שלך!" - pitch_body: "אנו פיתחנו את המשחק באנגלית, אבל יש הרבה שחקנים מכול העולם. חלק מהם רוצים לשחק בעברית והם לא מבינים אנגלית. אם אתה דובר את שני השפות, עברית ואנגלית, אז בבקשה עזור לנו לתרגם לעברית את האתר ואת השלבים." + sub_heading: "אנחנו צריכים את כישורי השפה שלך!" + pitch_body: "פיתחנו את המשחק באנגלית, אבל יש הרבה שחקנים מכול העולם. חלק מהם רוצים לשחק בעברית והם לא מבינים אנגלית. אם אתה דובר את שני השפות, עברית ואנגלית, אז בבקשה עזור לנו לתרגם לעברית את האתר ואת השלבים." missing_translations: "עד שנתרגם הכול לעברית, מה שלא תורגם יופיע באנגלית." learn_more: "תלמד עות על תרומת דיפלומטיה" subscribe_as_diplomat: "הירשם כדיפלומט" @@ -49,400 +50,643 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", play: play_as: "שחק בתור " # Ladder page spectate: "צופה" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + players: "שחקנים" # Hover over a level on /play + hours_played: "שעות משחק" # Hover over a level on /play + items: "כלים" # Tooltip on item shop button from /play + unlock: "קנה" # For purchasing items and heroes + confirm: "אשר" + owned: "נרכש" # For items you own + locked: "נעול" + purchasable: "ניתן לרכישה" # For a hero you unlocked but haven't purchased + available: "זמין" + skills_granted: "ממיומנויות נרכשו" # Property documentation details + heroes: "דמויות" # Tooltip on hero shop button from /play + achievements: "הישגים" # Tooltip on achievement list button from /play + account: "חשבון" # Tooltip on account button from /play + settings: "הגדרות" # Tooltip on settings button from /play + poll: "סקר" # Tooltip on poll button from /play + next: "הבא" # Go from choose hero to choose inventory before playing a level + change_hero: "שנה גיבור" # Go back from choose inventory to choose hero + choose_inventory: "לבש כלים" + buy_gems: "רכש אבני חן" + subscription_required: "יש צורך במנוי" + anonymous: "משתמש אנונימי" level_difficulty: "רמת קושי: " campaign_beginner: "מסע המתחילים" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "בחר את השלב" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "אתה יכול לבחור איזה שלב שאתה רוצה למטה, או לדון על שלבים ב" - adventurer_forum: "פורום ההרפתקנים" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "...שבו תלמד את קסם התכנות." - campaign_dev: "שלבים אקראים קשים יותר" - campaign_dev_description: "...שבהם תלמד על הממשק בזמן שתעשה משהו קצת קשה יותר." + awaiting_levels_adventurer_prefix: ".אנחנו מוסיפים חמישה שלבים בכל שבוע" # {change} + awaiting_levels_adventurer: "הירשם כהרפתקן" + awaiting_levels_adventurer_suffix: ".כדי להיות הראשון שישחק בשלבים חדשים" + adjust_volume: "שנה ווליום" campaign_multiplayer: "זירות רב-המשתתפים" campaign_multiplayer_description: "..." - campaign_player_created: "תוצרי השחקנים" - campaign_player_created_description: "... שבהם תילחם נגד היצירתיות של <a href=\"/contribute#artisan\">בעלי-המלאכה</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: ".אתה מתקדם מצויין! ספר למישהו כמה למדת" + email_invalid: ".כתובת המייל שהוזנה שגויה" + form_blurb: "!הזן כתובת מייל ואנחנו נראה להם" + form_label: "כתובת דואר אלקטרוני" + placeholder: "כתובת המייל" + title: "עבודה מצויינת, מתלמד" login: sign_up: "הירשם" - log_in: "היכנס" -# logging_in: "Logging In" + log_in: "התחבר" + logging_in: "מתחבר" log_out: "צא" - recover: "שחזר סיסמה" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "שכחתי סיסמא" + authenticate_gplus: "G+ אמת באמצעות" + load_profile: "G+ טען פרופיל" + finishing: "מסיים" + sign_in_with_facebook: "Facebook היכנס באמצעות" + sign_in_with_gplus: "G+ היכנס באמצעות" + signup_switch: "?רוצה ליצור חשבון" signup: - create_account_title: "הירשם כדי לשמור את התקדמותך" - description: "זה בחינם. רק כמה דברים וסיימנו:" email_announcements: "קבל הודעות באימייל" - coppa: "בן יותר משלוש עשרה או לא בארצות הברית" - coppa_why: "(למה?)" creating: "יוצר חשבון..." sign_up: "הירשם" log_in: "כנס עם סיסמה" -# social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." + social_signup: "Facebook ו Google+ או, שאתה יכול להירשם דרך" + required: ".יש להתחבר על מנת לגשת לשלב זה" + login_switch: "? כבר יש לך משתמש" recover: recover_account_title: "שחזר סיסמה" send_password: "שלח סיסמה חדשה" -# recovery_sent: "Recovery email sent." + recovery_sent: "מייל לשחזור סיסמא נשלח" -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "ראשי" + secondary: "משני" + armor: "שריון" + accessories: "אביזרים" + misc: "אחר" + books: "ספרי כישוף" common: + back: "חזור" # When used as an action verb, like "Navigate backward" + continue: "המשך" # When used as an action verb, like "Continue forward" loading: "...טוען" saving: "...שומר" sending: "...שולח" -# send: "Send" + send: "שלח" cancel: "ביטול" save: "שמור" -# publish: "Publish" -# create: "Create" + publish: "פרסם" + create: "צור" manual: "מדריך" fork: "קילשון" play: "שחק" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + retry: "נסה שוב" + actions: "פעולות" + info: "מידע" + help: "עזרה" + watch: "צפה" + unwatch: "הסר צפיה" + submit_patch: ".שלח תיקון" + submit_changes: "שלח שינויים" + save_changes: "שמור שינויים" -# general: -# and: "and" -# name: "Name" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" -# or: "or" -# subject: "Subject" -# email: "Email" -# password: "Password" -# message: "Message" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + general: + and: "וגם" + name: "שם" + date: "Date" + body: "גוף" + version: "גרסה" + pending: "ממתין" + accepted: "התקבל" + rejected: "נדחה" + withdrawn: "האוייב נסוג" + submitter: "מוסר" + submitted: "נמסר" + commit_msg: "שלח הודעה" + review: "צפה בהודעה" + version_history: "היסטורית גרסאות" + version_history_for: " :הסטוריית גרסאות ל" + select_changes: ".בחר בשני שינויים כדי לראות את ההבדל" + undo_prefix: "Undo" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Redo" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "הצג תצוגה מקדימה לשלב הנוכחי" + result: "תוצאה" + results: "תוצאות" + description: "תיאור" + or: "או" + subject: "נושא" + email: "מייל" + password: "סיסמה" + message: "הודעה" + code: "קוד" + ladder: "סולם" + when: "כש" + opponent: "יריב" + rank: "דרגה" + score: "ניקוד" + win: "ניצחון" + loss: "הפסד" + tie: "שוויון" + easy: "קל" + medium: "בינוני" + hard: "קשה" + player: "שחקן" + player_level: "רמה" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "לוחם" + ranger: "קשת" + wizard: "מכשף" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "שנייה" + seconds: "שניות" + minute: "דקה" + minutes: "דקות" + hour: "שעה" + hours: "שעות" + day: "יום" + days: "ימים" + week: "שבוע" + weeks: "שבועות" + month: "חודש" + months: "חודשים" + year: "שנה" + years: "שנים" -# play_level: -# done: "Done" -# home: "Home" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" -# guide: "Guide" -# restart: "Restart" -# goals: "Goals" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" -# action_timeline: "Action Timeline" -# click_to_select: "Click on a unit to select it." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" -# reload_title: "Reload All Code?" -# reload_really: "Are you sure you want to reload this level back to the beginning?" -# reload_confirm: "Reload All" -# victory_title_prefix: "" -# victory_title_suffix: " Complete" -# victory_sign_up: "Sign Up to Save Progress" -# victory_sign_up_poke: "Want to save your code? Create a free account!" -# victory_rate_the_level: "Rate the level: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" -# victory_go_home: "Go Home" # Only in old-style levels. -# victory_review: "Tell us more!" # Only in old-style levels. -# victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" -# guide_title: "Guide" -# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. -# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. -# tome_other_units: "Other Units" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). -# tome_select_a_thang: "Select Someone for " -# tome_available_spells: "Available Spells" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." + play_level: + done: "סיים" + home: "בית" # Not used any more, will be removed soon. + level: "שלב" # Like "Level: Dungeons of Kithgard" + skip: "דלג" + game_menu: "תפריט משחק" + guide: "מדריך" + restart: "נסה שוב" + goals: "מטרות" + goal: "מטרה" + running: "...פועל" + success: "!הצלחה" + incomplete: "לא הושלם" + timed_out: "הזמן אזל" + failing: "נכשל" + action_timeline: "ציר זמן" + click_to_select: ".לחץ על יחידה על מנת לבחור בה" + control_bar_multiplayer: "רב-משתתפים" + control_bar_join_game: "הצטרף למשחק" + reload: "טען שוב" + reload_title: "?לטעון מחדש את כל הקוד" + reload_really: "?אתה בטוח שאתה רוצה לטעון שוב את השלב הזה ולהתחיל מההתחלה" + reload_confirm: "טען הכול שוב" + victory: "ניצחון" + victory_title_prefix: "" + victory_title_suffix: " הושלם" + victory_sign_up: "הירשם על מנת לשמור התקדמות" + victory_sign_up_poke: "!רוצה לשמור את הקוד? הירשם בחינם עכשיו" + victory_rate_the_level: " :דרג את השלב" # Only in old-style levels. + victory_return_to_ladder: "חזור לסולם" + victory_play_continue: "המשך" + victory_saving_progress: "שומר התקדמות" + victory_go_home: "חזור הביתה" # Only in old-style levels. + victory_review: "!ספר לנו עוד" # Only in old-style levels. + victory_hour_of_code_done: "?סיימת" + victory_hour_of_code_done_yes: "שלי Hour of Code™! כן, סיימתי עם ה" + victory_experience_gained: "שנצבר XP" + victory_gems_gained: "אבני חן שנצברו" + victory_new_item: "חפץ חדש" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." + victory_become_a_viking: "הפוך לוויקינג" + guide_title: "מדריך" + tome_minion_spells: "הכישופים של המשרתים שלך" # Only in old-style levels. + tome_read_only_spells: "כישופים לקריאה בלבד" # Only in old-style levels. + tome_other_units: "יחידות אחרות" # Only in old-style levels. + tome_cast_button_run: "הפעל" + tome_cast_button_running: "פועל" + tome_cast_button_ran: "הופעל" + tome_submit_button: "שלח" + tome_reload_method: ".טען קוד מקורי לפונקציה זו" # Title text for individual method reload button. + tome_select_method: "בחר פונקציה" + tome_see_all_methods: "ראה את כל הפונקציות הניצנות לעריכה" # Title text for method list selector (shown when there are multiple programmable methods). + tome_select_a_thang: " בחר מישהו ל" + tome_available_spells: "כישופים זמינים" + tome_your_skills: "מיומנויות" + tome_help: "עזרה" + tome_current_method: "פונקציה נוכחית" + hud_continue_short: "המשך" + code_saved: "קוד נשמר" + skip_tutorial: "(esc) דלג" + keyboard_shortcuts: "קיצורי מקשים" + loading_ready: "!מוכן" + loading_start: "התחל שלב" + problem_alert_title: "תקן שגיאות בקוד" + problem_alert_help: "עזרה" + time_current: ":זמו נוכחי" + time_total: ":שמן מקסימלי" + time_goto: ":לך ל" + non_user_code_problem_title: "טעינת שלב נכשלה" + infinite_loop_title: "לופ אינסופי זוהה" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." + check_dev_console_link: "(הוראות)" + infinite_loop_try_again: "נסה שוב" + infinite_loop_reset_level: "התחל שלב מחדש" + infinite_loop_comment_out: ".הפוך את הקוד שלי להערה" + tip_toggle_play: "Ctrl+P הדלק הפעל/השהה בעזרת" + tip_scrub_shortcut: "Ctrl+[ ו Ctrl+] האץ אחורה וקדימה באמצעות" # {change} + tip_guide_exists: ".לחץ על המדריך, בתוך תפריט המשחק (למעלה בקצה העמוד), למידע שימושי" + tip_open_source: "!הוא 100% קוד פתוח CodeCombat" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: ".הפיץ את גרסת הבטא באוקטובר, 2013 CodeCombat" + tip_think_solution: ".תחשוב על הפתרון, לא על הבעיה" + tip_theory_practice: "תיאורטית, אין הבדל בין התאוריה לאימון. אבל באימון, יש. - יוגי ברה" + tip_error_free: "יש שתי דרכים לכתוב תוכנות בלי אף תקלה; רק השלישית עובדת. - אלן פרליס" + tip_debugging_program: "אם ניפוי הוא התהליך של הסרת באגים, אז תכנות חייב להיות התהליך של לשים אותם -. אדגר ו. דיקסטרא" + tip_forums: "!לך לפורומים וספר לנו מה אתה חושב" + tip_baby_coders: ".בעתיד, אפילו תינוקות יהיו כשפי על" + tip_morale_improves: ".הטעינה תמשיך עד שהמורל ישתפר" + tip_all_species: ".אנחנו מאמינים בשוויון הזדמנויות לכל היצורים בלמידת תכנות" # tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." + tip_harry: " ,תה' מכשף" + tip_great_responsibility: "עם כישרון גדול בתכנות באה גם אחריות דיבאגינג גדולה." + tip_munchkin: "אם לא תאכל את הירקות, מאצ'קין יבוא אליך בלילה כשאתה ישן." + tip_binary: "יש רק 10 אנשים בעולם: כאלה שמבינים בינארית וכאלה שלא." + tip_commitment_yoda: "מתכנת חייב להיות עם המחויבות העמוקה ביותר, עם המחשבה הרצינית ביותר. ~ יודה" + tip_no_try: "תעשה. או אל תעשה. אין לנסות - יודה" + tip_patience: "עליך להיות סבלני, פאדאוואן צעיר - יודה" + tip_documented_bug: "באג מתועד הוא לא באג; הוא בונוס שבא עם התוכנה." + tip_impossible: "זה תמיד נראה בלתי אפשרי עד שזה נעשה. - נלסון מנדלה" + tip_talk_is_cheap: "דיבור זה זול. תראה לי את הקוד - לינוס טורבאלדס" + tip_first_language: "הדבר ההרסני ביותר שאי פעם תלמד הוא שפת התכנות הראשונה שלך. - אלן קיי" + tip_hardware_problem: "ש: כמה מתכנתים צריך כדי להחליף נורה? ת: אפס, זו בכלל בעיה בחומרה." # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." + tip_superpower: ".תכנות זה הדבר הקרוב ביותר שיש לנו לכוחות על" +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" + tip_no_code: "לא קוד מהיר יותר מקוד של לא." + tip_code_never_lies: "קוד אף פעם לא משקר, הערות לפעמים כן. - רון ג'פריס" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" + tip_move_forward: "מה שלא תעשה, תמשיך להתקדם - מרטין לותר קינג ג'וניור" + tip_google: "!יש בעיה שאתה לא מצליח לפתור? תגגל אותה" + tip_adding_evil: ".מוסיף קמצוץ של רשע" +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." -# game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" -# multiplayer_tab: "Multiplayer" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + game_menu: + inventory_tab: "תיק" + save_load_tab: "טען/שמור" + options_tab: "אפשרויות" + guide_tab: "מדריך" + guide_video_tutorial: "סרטוני הדרכה" + guide_tips: "טיפים" + multiplayer_tab: "רב משתתפים" + auth_tab: "הירשם" + inventory_caption: "צייד את הגיבור שלך" + choose_hero_caption: "בחר גיבור, שפה" + save_load_caption: "וצפה בהיסטוריה..." + options_caption: "שנה הגדרות" + guide_caption: "מסמכים וטיפים" + multiplayer_caption: "!שחק עם חברים" + auth_caption: "שמור שינויים" -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "שיאים" + view_other_solutions: "צפה בפתרונות אחרים" # {change} + scores: "תוצאות" + top_players: "סדר שחקנים טובים ביותר לפי" + day: "היום" + week: "השבוע" + all: "אי-פעם" + time: "זמן" + damage_taken: "נזק שנגרם לשחקן" + damage_dealt: "מזק שהשחקן גרם" + difficulty: "רמת קושי" + gold_collected: "זהב שנאסף" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "הצטייד בכלים" + equipped_item: "מצוייד" + required_purchase_title: "נדרש" + available_item: "זמין" + restricted_title: "מוגבל" + should_equip: "(לחץ פעמיים כדי להצטייד/ללבוש)" + equipped: "(מצוייד/לבוש)" + locked: "(נעול)" + restricted: "(מוגבל בשלב הזה)" + equip: "לבש/הצטייד" + unequip: "הסר" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + buy_gems: + few_gems: "כמה אבני חן" + pile_gems: "ערמת אבני חן" + chest_gems: "תיבת אבני חן" + purchasing: "...קונה" + declined: ".כרטיס האשראי נדחה" + retrying: ".תקלה בשרת, מנסה שוב" + prompt_title: ".אין לך מספיק אבני חן" + prompt_body: "?רוצה לקבל עוד" + prompt_button: "היכנס לחנות" + recovered: ".רכישה אחרונה שוחזרה. טען את הדף מחדש בבקשה" + price: "x3500 לחודש" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + subscribe: + comparison_blurb: ".CodeCombatחדד את כישוריך עם מנוי ל" + feature1: "60+ שלבים בסיסיים ב 4 עולמות שונים." # {change} + feature2: "!עם כישורים מיוחדים <strong>גיבורים עוצמתיים חדשים</strong> 7" # {change} + feature3: "30+ שלבי בונוס" # {change} + feature4: "!בחינם כל חודש <strong>3500 אבני חן</strong>" + feature5: "הדרכות וידאו" + feature6: "תמיכת מייל בעדיפות ראשונה" + feature7: "פרטיים <strong>שבטים</strong>" + free: "חינם" + month: "חודש" + subscribe_title: "רכוש מנוי" + unsubscribe: "הסר מנוי" + confirm_unsubscribe: "אשר הסרת מנוי" + never_mind: "!לא משנה, אני עדיין אוהב אותך" + thank_you_months_prefix: ".תודה שתמכת בנו בחודשים האחרונים" + thank_you_months_suffix: "" + thank_you: "CodeCombat תודה על התמיכה ב" + sorry_to_see_you_go: ".מצטערים לראות אותך הולך! ספר לנו בבקשה מה יכולנו לשפר" + unsubscribe_feedback_placeholder: "?או, מה עשינו" + parent_button: "שאל את ההורים" + parent_email_description: ".עבורך CodeCombat אנחנו נדאג לשלוח להם מייל כדי שיקנו מנוי" + parent_email_input_invalid: ".כתובת האי-מייל שהוזנה שגויה" + parent_email_input_label: "כתובת מייל של ההורה" + parent_email_input_placeholder: "הכנס כתובת מייל של ההורה" + parent_email_send: "שלח מייל" + parent_email_sent: "מייל נשלח" + parent_email_title: "?מהו המייל של הוריך" + parents: "להורים" + parents_title: "ילדכם ילמד לתכנת." # {change} +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "מנוי חודשי" + subscription_required_to_play: "יש צורך במנוי כדי לשחק בשלב זה." + unlock_help_videos: "רכוש מנוי כדי לפתוח את כל הדרכות הוידאו." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + choose_hero: + choose_hero: "בחר גיבור" + programming_language: "שפת תכנות" + programming_language_description: "?באיזו שפת תכנות תרצה להשתמש" + default: ":ברירת מחדל" + experimental: "נסיוני" + python_blurb: "שפה פשוטה אך עוצמתית, מצויינת למתחילים ולמתקדמים" + javascript_blurb: "(.Java-השפה של הרשת. (שונה מ" + coffeescript_blurb: ".JavaScript-תחביר יפה יותר ל" + clojure_blurb: ".עילגות מודרנית" + lua_blurb: ".שפה לכתיבת תסריטי משחק" + io_blurb: ".פשוטה אך מעורפלת" + status: "סטטוס" + hero_type: "סוג" + weapons: "נשקים" + weapons_warrior: "חרבות, טווח קצר - ללא קסם" + weapons_ranger: "קשתות, אקדחים - טווח ארוך, ללא קסם" + weapons_wizard: "שרביטים, מטות קסם - טווח ארוך, קסם" + attack: "עוצמה" # Can also translate as "Attack" + health: "נקודות חיים" + speed: "מהירות" + regeneration: "השתקמות" + range: "טווח" # As in "attack or visual range" + blocks: "הגנה" # As in "this shield blocks this much damage" + backstab: "פגיעה בגב" # As in "this dagger does this much backstab damage" + skills: "מיומנויות" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." + speed_1: "זז במהירות של" + speed_2: "מטרים לשנייה." + available_for_purchase: "ניתן לרכישה" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: ":שלב כדי לפתוח" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: ".רק גיבורים מסויימים יכולים לשחק בשלב זה" -# options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." -# editor_config: "Editor Config" -# editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" + skill_docs: + writable: "ניתן לכתיבה" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "לקריאה בלבד" + action_name: "שם" + action_cooldown: "לוקח" + action_specific_cooldown: ":זמן להתקררות" + action_damage: "עוצמה" + action_range: "טווח" + action_radius: "רדיוס" + action_duration: "משך זמן" + example: "דוגמה" + ex: "כגון" # Abbreviation of "example" + current_value: "ערך נוכחי" + default_value: "ערך רגיל" + parameters: "פרמטרים" + returns: "מחזיר" + granted_by: "הוענק ע\"י" + + save_load: + granularity_saved_games: "נשמר" + granularity_change_history: "היסטוריה" + + options: + general_options: "הגדרות כלליות" # Check out the Options tab in the Game Menu while playing a level + volume_label: "ווליום" + music_label: "מוזיקה" + music_description: "כבה/הפעל מוזיקת רקע" + editor_config: "תצורת(קונפיגורצית) עורך" + editor_config_title: "תצורת(קונפיגורצית) עורך" + editor_config_level_language_label: "שפה לשלב זה" + editor_config_level_language_description: ".בחר את שפת התכנות בה אתה תשתמש בשלב זה" + editor_config_default_language_label: "ברירת מחדל" + editor_config_default_language_description: ".בחר רת שפת התכנות בה תרצה להשתמש בהתחלת שלבים חדשים" + editor_config_keybindings_label: "(תצורה(קונפיגורציה" + editor_config_keybindings_default: "(Ace) ברירת מחדל" # editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." # editor_config_livecompletion_label: "Live Autocompletion" # editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." + editor_config_invisibles_label: "הצג בלתי נראים" + editor_config_invisibles_description: "הצג בלתי נראים כמו רווחים וטאבים." # editor_config_indentguides_label: "Show Indent Guides" # editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" + editor_config_behaviors_label: "התנהגוץ חכמה" # editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." -# about: -# why_codecombat: "Why CodeCombat?" -# why_paragraph_1: "If you want to learn to program, you don't need lessons. You need to write a lot of code and have a great time doing it." -# why_paragraph_2_prefix: "That's what programming is about. It's gotta be fun. Not fun like" -# why_paragraph_2_italic: "yay a badge" -# why_paragraph_2_center: "but fun like" -# why_paragraph_2_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" + about: + why_codecombat: "?CodeCombat למה" + why_paragraph_1: "אם אתה רוצה ללמוד לתכנת, אתה לא צריך שיעורים. אתה רק צריך לכתוב המון קוד ושתהנה בזה." + why_paragraph_2_prefix: "על זה מדבר תכנות. זה יהיה כיף. לא כיף כמו" + why_paragraph_2_italic: "יאי מדליה" + why_paragraph_2_center: "אלא כיף כמו" + why_paragraph_2_italic_caps: "לא אמא אני צריך לסיים את השלב!" # why_paragraph_2_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." # why_paragraph_3: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." # press_title: "Bloggers/Press" # press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + team: "צוות" + george_title: "מנכ\"ל" # {change} + george_blurb: "ביזנסר" + scott_title: "מתכנת" # {change} + scott_blurb: "אחד סביר" + nick_title: "מתכנת" # {change} + nick_blurb: "גורו מוטיבציה" + michael_title: "מתכנת" + michael_blurb: "מנהל מערכת" + matt_title: "מתכנת" # {change} + matt_blurb: "רוכב אופניים" + cat_title: "צ'יף אמן" + cat_blurb: "כשף אוויר" + josh_title: "מעצב משחק" + josh_blurb: "ארצפה היא לבה" + jose_title: "מוזיקה" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "שמור גרסה חדשה" new_major_version: "גרסה חשובה חדשה" + submitting_patch: "...שולח תיקון" cla_prefix: "כדי לשמור יש להירשם לאתר" -# cla_url: "CLA" -# cla_suffix: "." + cla_url: "CLA" + cla_suffix: "." cla_agree: "אני מסכים" + owner_approve: "אישור בעלים יתבקשת לפני שהשינויים יחולו." contact: contact_us: "צור קשר" welcome: "טוב לשמוע ממך! השתמש בטופס זה כדי לשלוח לנו אימייל. " - contribute_prefix: "אם אתה מעונין לתרום, אז תבדוק את " - contribute_page: "דף התרומות שלנו" - contribute_suffix: "!" forum_prefix: "בשביל דברים ציבוריים, לך ל " forum_page: "פורום שלנו" forum_suffix: " במקום." - send: "שלח אימייל" -# contact_candidate: "Contact Candidate" # Deprecated -# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated + faq_prefix: "יש גם" + faq: "שאלות נפוצות" + subscribe_prefix: ",אם אתם צריכים עזרה בלעבור שלב כלשהו בבקשה" + subscribe: "CodeCombatקנו מנוי ל" + subscribe_suffix: "ואנו נשמח לעזור לך עם הקוד" + subscriber_support: ".מכיוון שאתה מנוי, המייל שלך יקבל עדיפות לתמיכה" + screenshot_included: ".כולל תמונות" + where_reply: "?איפה אנחנו אמורים להגיב" + send: "שלח מייל" + contact_candidate: "צור קשר עם נציג" # Deprecated + recruitment_reminder: "השתמש בטופס זה כדי להגיע למועמדים שאתה מעוניין לראיין. זכור כי CodeCombat גובה 15% מהשכר בשנה הראשונה.התשלום מתבצע בעת שכירת העובדים ויוחזר במשך 90 ימים אם העובד לא יישאר מועסק. משרה חלקית, עובדים מרוחקים, עובדי חוזה הם חינם, כמו גם מתמחה." # Deprecated account_settings: title: "הגדרות חשבון" @@ -450,23 +694,30 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", autosave: "שינויים נשמרו אוטומטית" me_tab: "אני" picture_tab: "תמונה" -# upload_picture: "Upload a picture" + delete_account_tab: "מחק את חשבונך" + wrong_email: "כתובת מייל שגויה." + wrong_password: "סיסמא שגוייה" + upload_picture: "העלה תמונה" + delete_this_account: "מחק את חשבון זה לתמיד" + god_mode: "מצב אל" password_tab: "סיסמה" emails_tab: "אימיילים" admin: "אדמין" new_password: "סיסמה חדשה" new_password_verify: "חזור על הסיסמה שנית" + type_in_email: "הזן את כתובת המייל שלך על מנת לבצע את המחיקה" # {change} + type_in_password: "גםת הזן את סיסמתך." email_subscriptions: "הרשמויות אימייל" -# email_subscriptions_none: "No Email Subscriptions." + email_subscriptions_none: ".אין מנויי מייל" email_announcements: "הודעות" email_announcements_description: "קבל את החדשות ואת הפיתוחים הכי חדישים במשחק באימייל." email_notifications: "עדכונים" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." + email_notifications_summary: ".CodeCombatכלים לעדכוני מייל, מותאמים אישית על הפעילות שלכם ב" + email_any_notes: "כל סוגרות" + email_any_notes_description: "נטרל כדי להפסיק את עדכוני הפעילות באי-מייל" + email_news: "חדשות" + email_recruit_notes: "הזדמנויות עבודה" + email_recruit_notes_description: ".(אם אתם משחקים טוב באמת, יש סיכוי שניצור איתכם קשר על מנת למצוא לכך עבודה (טובה יותר" contributor_emails: "אימיילים של כיתות תורמים" contribute_prefix: "אנו מחפשים אנשים שיצתרפו למסיבה! תראו את" contribute_page: "דף התרימות" @@ -475,166 +726,229 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", error_saving: "בעיה בשמירה" saved: "השינויים נשמרו" password_mismatch: "סיסמאות לא זהות" -# password_repeat: "Please repeat your password." -# job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated -# job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." -# job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - wizard_tab: "קוסם" - wizard_color: "צבע הקוסם" + password_repeat: ".אנא חזור על הסיסמא" + job_profile: "פרופיל עבודה" # Rest of this section (the job profile stuff and wizard stuff) is deprecated + job_profile_approved: "פרופיל העבודה שלך אושר על ידי CodeCombat. מעסיקים יוכלו לראות את הפרופיל עד שאתה או מסמן אותו כ 'לא פעיל' או אם הוא לא השתנה במשך ארבעה שבועות." + job_profile_explanation: "היי! מלא את זה, ואנו ניצור איתך קשר על מציאת עבודה כמפתח תוכנה." + sample_profile: "פרופיל לדוגמה" + view_profile: "צפה בפרופיל שלך" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." + keyboard_shortcuts: + keyboard_shortcuts: "קיצורי מקשים" + space: "Space" + enter: "Enter" + press_enter: "לחץ enter" + escape: "Escape" + shift: "Shift" + run_code: ".הרץ קוד נוכחי" + run_real_time: ".הרץ קוד בזמן אמת" + continue_script: ".המשך בתסריט זה" + skip_scripts: ".דלג על כל התסריטים שניתן לדלג עליהם" + toggle_playback: ".הפעלהשהה" + scrub_playback: ".התקדם אחורה וקדימה בזמן" + single_scrub_playback: ".התקדם אחורה וקדימה בזמן בפריים אחד" # scrub_execution: "Scrub through current spell execution." # toggle_debug: "Toggle debug display." # toggle_grid: "Toggle grid overlay." # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" + community: + main_title: "CodeCombat קהילת" + introduction: "!תבדוק את הדרכים שאנחנו מציעים לך להיות מעורב ותבחא את הדרך שנראית לך הכי כיפית. אנחנו מצפים לעבוד איתך" + level_editor_prefix: "CodeCombat תשתמש ב" # level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" + thang_editor_prefix: "אנחנו קוראים ליחידות בתוך המשחק ,ת\"ת'אנגס\" השתמש ב " # thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." # article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" # article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + find_us: ":מצא אותנו באתרים האלה" + social_blog: "Sett ב CodeCombat קרא את הבלוג של" + social_discource: "שלנו Discourse forum הצטרף לדיון ב" + social_facebook: "Facebook ב CodeCombat תן לייק ל" + social_twitter: "Twitter ב CodeCombat תעקוב אחרי" + social_gplus: "Google+ ב CodeCombat הצטרף ל" + social_hipchat: "CodeCombat הציבורי של HipChat room שוחח בצ'אט איתנו ב" + contribute_to_the_project: "תרום לפרוייקט" -# classes: -# archmage_title: "Archmage" -# archmage_title_description: "(Coder)" -# artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" -# scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" -# diplomat_title_description: "(Translator)" -# ambassador_title: "Ambassador" -# ambassador_title_description: "(Support)" + clans: + clan: "שבט" + clans: "שבטים" + new_name: "שם שבט חדש" + new_description: "תיאור שבט חדש" + make_private: "הפוך שבט לפרטי" + subs_only: "מנויים בלבד" + create_clan: "צור שבט חדש" + private_preview: "צפה" + public_clans: "שבטים פתוחים" + my_clans: "השבטים שלי" + clan_name: "שם השבט" + name: "שם" + chieftain: "צ'יף(מנהיג)" + type: "סוג" + edit_clan_name: "ערוך את שם השבט" + edit_clan_description: "ערוך את תיאור השבט" + edit_name: "ערוך שם" + edit_description: "ערוך תיאור" + private: "(פרטי)" + summary: "תקציר" + average_level: "רמה ממוצעת" + average_achievements: "מס' הישגם ממוצע" + delete_clan: "מחק שבט" + leave_clan: "עזוב שבט" + join_clan: "הצטרף לשבט" + invite_1: "הזמן:" + invite_2: "*הזמן שחקנים לשבט באמצעות שליחת הקישור הזה." + members: "חברים" + progress: "התקדמות" + not_started_1: "לא הותחל" + started_1: "הותחל" + complete_1: "סויים" + exp_levels: "הרחב שלבים" + rem_hero: "הסר גיבור" + status: "סטטוס" + complete_2: "סויים" + started_2: "הותחל" + not_started_2: "לא הותחל" + view_solution: "לחץ כדי לראות פתרונות." + latest_achievement: "הישגים עדכניים" + playtime: "זמן משחק" + last_played: "שיחק לאחרונה" -# editor: -# main_title: "CodeCombat Editors" -# article_title: "Article Editor" -# thang_title: "Thang Editor" -# level_title: "Level Editor" -# achievement_title: "Achievement Editor" -# back: "Back" -# revert: "Revert" -# revert_models: "Revert Models" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." -# generate_terrain: "Generate Terrain" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" -# level_some_options: "Some Options?" -# level_tab_thangs: "Thangs" -# level_tab_scripts: "Scripts" -# level_tab_settings: "Settings" -# level_tab_components: "Components" -# level_tab_systems: "Systems" -# level_tab_docs: "Documentation" -# level_tab_thangs_title: "Current Thangs" -# level_tab_thangs_all: "All" -# level_tab_thangs_conditions: "Starting Conditions" -# level_tab_thangs_add: "Add Thangs" -# delete: "Delete" -# duplicate: "Duplicate" -# rotate: "Rotate" -# level_settings_title: "Settings" -# level_component_tab_title: "Current Components" -# level_component_btn_new: "Create New Component" -# level_systems_tab_title: "Current Systems" -# level_systems_btn_new: "Create New System" -# level_systems_btn_add: "Add System" -# level_components_title: "Back to All Thangs" -# level_components_type: "Type" -# level_component_edit_title: "Edit Component" + classes: + archmage_title: "כשף על" + archmage_title_description: "(מתכנת)" + archmage_summary: "!אם אתה מפתח שמעוניין בתכנות משחקי לימוד קוד חינוכיים, הפוך לרב-כשף ועזור לנו לבנות את המשחק" + artisan_title: "אמן" + artisan_title_description: "(בונה שלבים)" + artisan_summary: ".בנה ושתף שלבים שאתה וחבריך תשחקו בהם. הפוך לאמן כדי ללמוד את האומנות של לימוד תכנות לאחרים" + adventurer_title: "הרפתקן" + adventurer_title_description: "(שחקן שלבים ניסיוניים)" + adventurer_summary: ".השג את השלבים החדשים שלנו (כולל את תוכן המנןי) שבוע לפני כולם ועזור לנו לתקן את הבאגים שנמצאים בהם" + scribe_title: "סופר" + scribe_title_description: "(עורך כותרות)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." + diplomat_title: "דיפלומט" + diplomat_title_description: "(מתרגם)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." + ambassador_title: "שגריר" + ambassador_title_description: "(תמיכה)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." + + editor: + main_title: "CodeCombat עורך" + article_title: "עורך כותרות" + thang_title: "עורך ת'אנג" + level_title: "עורך שלבים" + achievement_title: "עורך הישגים" + poll_title: "עורך סקרים" + back: "חזרה" + revert: "שחזר" + revert_models: "שחזר מודלים" + pick_a_terrain: "בחר סוג שטח" + dungeon: "צינוק" + indoor: "בפנים" + desert: "מדבר" + grassy: "עשבי" +# mountain: "Mountain" +# glacier: "Glacier" + small: "קטן" + large: "רחב" + fork_title: "פצל גירסה חדשה" + fork_creating: "יוצר פיצול..." + generate_terrain: "צור שטח" + more: "עוד" + wiki: "ויקי" + live_chat: "צ'אט חי" + thang_main: "ראשי" +# thang_spritesheets: "Spritesheets" + thang_colors: "צבעים" + level_some_options: "?קצת אפשרויות" + level_tab_thangs: "ת'אנגס" + level_tab_scripts: "תסריטים" + level_tab_settings: "הגדרות" + level_tab_components: "מרכיבים" + level_tab_systems: "מערכות" + level_tab_docs: "דוקומנטציה" + level_tab_thangs_title: "ת'אנגס נוכחיים" + level_tab_thangs_all: "הכל" + level_tab_thangs_conditions: "תנאים התחלתיים" + level_tab_thangs_add: "הוסף ת'אנגס" + level_tab_thangs_search: "חפש ת'אנגס" + add_components: "הוסף מרכיבים" + component_configs: "קבע תצורת מרכיבים" + config_thang: "לחץ פעמיים כדי לשנות ת'אנג" + delete: "מחק" + duplicate: "שכפל" + stop_duplicate: "עצור שכפןל" + rotate: "סובב" + level_settings_title: "הגדרות" + level_component_tab_title: "מרכיבים נוכחיים" + level_component_btn_new: "צור מרכיב חדש" + level_systems_tab_title: "מערכת נוכחית" + level_systems_btn_new: "צור מערכת חדשה" + level_systems_btn_add: "הוסף מערכת" + level_components_title: "בחזרה לכל ה ת'אנגס" + level_components_type: "סוג" + level_component_edit_title: "ערוך מרכיב" # level_component_config_schema: "Config Schema" -# level_component_settings: "Settings" -# level_system_edit_title: "Edit System" -# create_system_title: "Create New System" -# new_component_title: "Create New Component" -# new_component_field_system: "System" -# new_article_title: "Create a New Article" -# new_thang_title: "Create a New Thang Type" -# new_level_title: "Create a New Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" -# article_search_title: "Search Articles Here" -# thang_search_title: "Search Thang Types Here" -# level_search_title: "Search Levels Here" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." + level_component_settings: "הגדרות" + level_system_edit_title: "מערכת עריכה" + create_system_title: "צור מערכת חדשה" + new_component_title: "צור מרכיב חדש" + new_component_field_system: "מערכת" + new_article_title: "צור כותרת חדשה" + new_thang_title: "צור סוג ת'אנג חדש" + new_level_title: "צור שלב חדש" + new_article_title_login: "התחבר כדי ליצור כותרת חדשה" + new_thang_title_login: "היכנס כדי ליצור סוג ת'אנג חדש" + new_level_title_login: "התחבר כדי ליצור שלב חדש" + new_achievement_title: "צור הישג חדש" + new_achievement_title_login: "התחבר כדי ליצור הישג חדש" + new_poll_title: "צור סקר חדש" + new_poll_title_login: "התחבר כדי ליצור סקר חדש" + article_search_title: "חפש כותרות כאן" + thang_search_title: "חפש סוגי ת'אנגס" + level_search_title: "חפש שלבים כאן" + achievement_search_title: "חפש הישגים" + poll_search_title: "חפש סקרים" + read_only_warning2: ".הערה: אין באפשרותך לשמור את העריכות שבוצעו כאן, אתה לא מחובר למערכת" + no_achievements: "אין הישגים שנוספו לשלב זה עד כה" # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" -# pop_i18n: "Populate I18N" + level_completion: "סיום שלב" + pop_i18n: "I18N אכלס" + tasks: "מטלות" + clear_storage: "נקה את השינויים המקומיים" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" -# article: -# edit_btn_preview: "Preview" -# edit_article_title: "Edit Article" + article: + edit_btn_preview: "צפה בכותרת" + edit_article_title: "ערוך כותרת" -# contribute: -# page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" + polls: + priority: "עדיפות" + + contribute: + page_title: "תרימה" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" + alert_account_message_intro: "!היי" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" -# archmage_attribute_1_pref: "Knowledge in " + archmage_attribute_1_pref: " ידע ב" # archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." # archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" -# join_desc_1: "Anyone can help out! Just check out our " + how_to_join: "כיצד להצטרף" + join_desc_1: " כל אחד יכול לעזור! רק תבדקו את ה" # join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " # join_desc_3: ", or find us in our " # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -695,181 +999,241 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # translating_diplomats: "Our Translating Diplomats:" # helpful_ambassadors: "Our Helpful Ambassadors:" -# ladder: -# please_login: "Please log in first before playing a ladder game." -# my_matches: "My Matches" -# simulate: "Simulate" -# simulation_explanation: "By simulating games you can get your game ranked faster!" -# simulate_games: "Simulate Games!" -# simulate_all: "RESET AND SIMULATE GAMES" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" -# battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " -# summary_wins: " Wins, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" -# rank_submitting: "Submitting..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" -# rank_last_submitted: "submitted " + ladder: + please_login: "התחבר בבקשה לפני שתשחק במשחק סולם." + my_matches: "המשחקים שלי" + simulate: "הדמה" + simulation_explanation: "באמצעות הדמית משחקים אתה יכול לגרום לשלב שלך להיות מדורג להר יותר!" + simulate_games: "הדמה משחקים" + simulate_all: "אתחל והדמה משחקים" + games_simulated_by: "משחקים שהדמית:" + games_simulated_for: "משחקים שהודמו בשבילך:" + games_simulated: "משחקים שהודמו" + games_played: "מס' משחקים ששיחקת" + ratio: "יחס" + leaderboard: "שיאים" + battle_as: "הילחם כ " + summary_your: "נתונים " + summary_matches: "משחקים - " + summary_wins: " ניצחונות, " + summary_losses: " הפסדים" + rank_no_code: "אין קוד חדש לדירוג" + rank_my_game: "דרג את המשחק שלי!" + rank_submitting: "שולח..." + rank_submitted: "נשלח לדירוג" + rank_failed: "נכשל לדרג" + rank_being_ranked: "משחק בדירוג" + rank_last_submitted: "נשלח " # help_simulate: "Help simulate games?" # code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." # no_ranked_matches_pre: "No ranked matches for the " # no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." -# choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" -# tutorial_play: "Play Tutorial" -# tutorial_recommended: "Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" -# tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" -# warmup: "Warmup" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" + choose_opponent: "בחר יריב" + select_your_language: "בחר שפה!" + tutorial_play: "הדרכה" + tutorial_recommended: "מומלץ אם לא שיחקת לפני" + tutorial_skip: "דלג על ההדרכה" + tutorial_not_sure: "לא בטוח מה הולך?" + tutorial_play_first: "הרץ את ההדרכה" + simple_ai: "בינה מלאכותית פשוטה" + warmup: "חימום" + friends_playing: "חברים משחקים" + log_in_for_friends: "היכנס כדי לשחק עם חברים" # social_connect_blurb: "Connect and play against your friends!" # invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" + fight: "הלחם!" + watch_victory: "צפה בניצחון שלך" + defeat_the: "הבס את ה" +# tournament_started: ", started" + tournament_ends: "תחרות נגמרת בעוד:" + tournament_ended: "תחרות נגמרה" + tournament_rules: "חוקי תחרות" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" + tournament_blurb_blog: "בבלוג שלנו" + rules: "חוקים" + winners: "מנצחים" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + user: + stats: "נתונים" + singleplayer_title: "שלבים לשחקן יחיד" + multiplayer_title: "שלבים מרובי משתתפים" + achievements_title: "הישגים" + last_played: "שוחק לאחרונה" + status: "סטטוס" + status_completed: "הושלם" + status_unfinished: "לא הושלם" + no_singleplayer: ".עדיין לא שיחקת באף שלב לשחקן יחיד" + no_multiplayer: ".עדיין לא שיחקת באף שלב מרובה משתתפים" + no_achievements: ".עדיין לא השגת אף הישג" + favorite_prefix: " שפה מועדפת היא" + favorite_postfix: "." + not_member_of_clans: "לא חבר באף שבט." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + achievements: + last_earned: "Last Earned" + amount_achieved: "כמות" + achievement: "הישג" + category_contributor: "מתנדב" + category_ladder: "סולם" + category_level: "שלב" + category_miscellaneous: "אחר" + category_levels: "שלבים" + category_undefined: "לא בקטגוריה" + current_xp_prefix: "" + current_xp_postfix: " נצבר בכללי" + new_xp_prefix: "" + new_xp_postfix: " נצבר" + left_xp_prefix: "" + left_xp_infix: " עד לרמה הבאה " + left_xp_postfix: "" -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." + account: + recently_played: "שוחק לאחרונה" + no_recent_games: "לא שיחקת במשחקים בשבועיים האחרונים." + payments: "תשלומים" + purchased: "קניות" + subscription: "מנויים" +# invoices: "Invoices" + service_apple: "Apple" + service_web: "רשת" + paid_on: "שולם על" + service: "שירות" + price: "מחיר" + gems: "אבני חן" + active: "פעיל" + subscribed: "מנוי" + unsubscribed: "לא מנוי" + active_until: "פעיל עד" + cost: "עולה" + next_payment: "התשלום הבא" + card: "כרטיס" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" -# loading_error: -# could_not_load: "Error loading from server" -# connection_failure: "Connection failed." -# unauthorized: "You need to be signed in. Do you have cookies disabled?" -# forbidden: "You do not have the permissions." -# not_found: "Not found." -# not_allowed: "Method not allowed." -# timeout: "Server timeout." + account_invoices: + amount: "כמות בדולרים (אמריקאיים)" + declined: "הכרטיסשלך בוטל" + invalid_amount: "אנא הזן כמות דולרים (אמריקאיים)" +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" + purchasing: "מבצע רכישה..." + retrying: "אירעה שגיאה בשרת, מנסה מחדש..." + success: "שולם בהצלחה, תודה!" + + loading_error: + could_not_load: "אירעה שגיאה בזמן טעינה מהשרת" + connection_failure: ".חיהור נכשל" + unauthorized: "אתה צריך להתחבר. האם חסמת קבצי cookies?" + forbidden: "אין לך את ההרשאה" + not_found: ".לא נמצא" + not_allowed: ".פונקציה לא מותרת" + timeout: "Server timeout." # conflict: "Resource conflict." # bad_input: "Bad input." -# server_error: "Server error." -# unknown: "Unknown error." + server_error: ".שגיאת שרת" + unknown: ".שגיאה בלתי מזוהה" -# resources: + resources: # sessions: "Sessions" # your_sessions: "Your Sessions" -# level: "Level" + level: "שלב" # social_network_apis: "Social Network APIs" -# facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" + facebook_status: "Facebook סטטוס" + facebook_friends: "Facebook חברי" # facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" + gplus_friends: "G+ חברי" # gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" + leaderboard: "לוח תוצאות" # user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" + user_profile: "פרופיל משתמש" + patch: "תיקון" + patches: "תיקונים" # patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" + model: "מודל" + system: "מערכת" + systems: "מערכות" + component: "מרכיב" + components: "מרכיבים" + thang: "Thang" + thangs: "Thangים" # level_session: "Your Session" # opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" + article: "כותרות" + user_names: "שמות משתמש" # thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" + files: "קבצים" + top_simulators: "סימולטורים מובילים" # source_document: "Source Document" -# document: "Document" + document: "מסמך" # sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" + employers: "מעסיקים" + candidates: "מועמדים" # candidate_sessions: "Candidate Sessions" # user_remark: "User Remark" # user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" -# feedback: "Feedback" + versions: "גרסאות" + items: "חפצים" +# hero: "Hero" + heroes: "גיבורים" + achievement: "הישגים" + clas: "CLAs" + play_counts: "כמות פעמים ששיחק" + feedback: "פידבק" + payment_info: "מידע תשלום" + campaigns: "מסעות" + poll: "סקר" + user_polls_record: "היסטוריית הצבעות בסקרים" -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" + + delta: + added: "נוסף" + modified: "שונה" +# not_modified: "Not Modified" + deleted: "נמחק" # moved_index: "Moved Index" # text_diff: "Text Diff" # merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" + no_changes: "אין שינויים" -# guide: -# temp: "Temp" + guide: + temp: "זמני" -# multiplayer: -# multiplayer_title: "Multiplayer Settings" # We'll be changing this around significantly soon. Until then, it's not important to translate. -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." -# multiplayer_link_description: "Give this link to anyone to have them join you." -# multiplayer_hint_label: "Hint:" + multiplayer: + multiplayer_title: "הגדרות רב-משתתפים" # We'll be changing this around significantly soon. Until then, it's not important to translate. + multiplayer_toggle: "אפשר רב-משתתפים" + multiplayer_toggle_description: ".אפשר לאחרים להצטרף למשחק" + multiplayer_link_description: ".מסור את הלינק הזה לכל אחד כדי שיצטרף אליך" + multiplayer_hint_label: "רמז:" # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,41 +1306,25 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "הגדרות קוסם" - customize_avatar: "עצב את הדמות שלך" -# active: "Active" -# color: "Color" -# group: "Group" - clothes: "בגדים" - trim: "קישוט" - cloud: "ענן" -# team: "Team" - spell: "כישוף" - boots: "מגפיים" - hue: "Hue" - saturation: "גוון" - lightness: "בהירות" - account_profile: -# settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" + settings: "הגדרות" # We are not actively recruiting right now, so there's no need to add new translations for this section. + edit_profile: "ערוך פרופיל" + done_editing: "סיים עריכה" profile_for_prefix: "פרופיל ל" profile_for_suffix: "" # featured: "Featured" # not_featured: "Not Featured" -# looking_for: "Looking for:" -# last_updated: "Last updated:" -# contact: "Contact" + looking_for: ":מחפש" + last_updated: "עודכן לאחרונה ב:" + contact: "צור קשר" # active: "Looking for interview offers now" # inactive: "Not looking for offers right now" # complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." -# next_name: "name?" -# next_short_description: "write a short description." + next: "הבא" + next_city: "עיר?" + next_country: "בחר את המדינה שלך." + next_name: "שם?" + next_short_description: "כתוב תיאור קצר." # next_long_description: "describe your desired position." # next_skills: "list at least five skills." # next_work: "chronicle your work history." @@ -1115,22 +1457,22 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # other_developers: "Other Developers" # inactive_developers: "Inactive Developers" -# admin: -# av_espionage: "Espionage" # Really not important to translate /admin controls. -# av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" -# av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" + admin: + av_espionage: "ריגול" # Really not important to translate /admin controls. + av_espionage_placeholder: "כתובת מייל או שם משתמש" + av_usersearch: "חיפןש משתמשים" + av_usersearch_placeholder: "מייל, שם משתמש, שם,סתם משהו" + av_usersearch_search: "חיפוש" # av_title: "Admin Views" # av_entities_sub_title: "Entities" -# av_entities_users_url: "Users" + av_entities_users_url: "משתמשים" # av_entities_active_instances_url: "Active Instances" -# av_entities_employer_list_url: "Employer List" + av_entities_employer_list_url: "רשימת מעסיק" # av_entities_candidates_list_url: "Candidate List" # av_entities_user_code_problems_list_url: "User Code Problems List" -# av_other_sub_title: "Other" + av_other_sub_title: "אחר" # av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "User List" -# ucp_title: "User Code Problems" -# lg_title: "Latest Games" -# clas: "CLAs" + u_title: "רשימת משתמשים" + ucp_title: "בעיות משתמשים בקוד" + lg_title: "המשחקים הכי חדשים" + clas: "CLAs" diff --git a/app/locale/hi.coffee b/app/locale/hi.coffee index 6f2b67f4d..bc1e0c362 100644 --- a/app/locale/hi.coffee +++ b/app/locale/hi.coffee @@ -1,50 +1,51 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDescription: "Hindi", translation: -# home: -# slogan: "Learn to Code by Playing a Game" -# no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older -# no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level + home: + slogan: "खेल खेल के द्वारा कोड करना सीखें" + no_ie: "CodeCombat Internet Explorer 8 या पुराने में नहीं चलता है| असुविधा के लिए खेद है!" # Warning that only shows up in IE8 and older + no_mobile: "CodeCombat मोबाइल उपकरणों के लिए तैयार नहीं है और काम नहीं कर सकता!" # Warning that shows up on mobile devices + play: "खेलें " # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." -# campaign: "Campaign" -# for_beginners: "For Beginners" -# multiplayer: "Multiplayer" # Not currently shown on home page -# for_developers: "For Developers" # Not currently shown on home page. -# or_ipad: "Or download for iPad" +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." + campaign: "अभियान" + for_beginners: "नौसिखिये के लिए" + multiplayer: "मल्टीप्लेयर" # Not currently shown on home page + for_developers: "डेवलपर्स के लिए" # Not currently shown on home page. + or_ipad: "या iPad के लिए डाउनलोड करें" -# nav: -# play: "Levels" # The top nav bar entry where players choose which levels to play -# community: "Community" -# editor: "Editor" -# blog: "Blog" -# forum: "Forum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" -# admin: "Admin" # Only shows up when you are an admin -# home: "Home" -# contribute: "Contribute" -# legal: "Legal" -# about: "About" -# contact: "Contact" -# twitter_follow: "Follow" -# teachers: "Teachers" + nav: + play: "चरण" # The top nav bar entry where players choose which levels to play + community: "समुदाय" + editor: "एडीटर" + blog: "ब्लॉग" + forum: "मंच" + account: "खाता" + profile: "रूपरेखा" + stats: "आँकड़े" + code: "कोड" + admin: "एडमिन" # Only shows up when you are an admin + home: "होम" + contribute: "योगदान करें" + legal: "कानूनी" + about: "बारे मे" + contact: "संपर्क" + twitter_follow: "साथ दें" + teachers: "शिक्षक" -# modal: -# close: "Close" -# okay: "Okay" + modal: + close: "बंद करें" + okay: "ठीक है" -# not_found: -# page_not_found: "Page not found" + not_found: + page_not_found: "पेज नहीं मिला" diplomat_suggestion: -# title: "Help translate CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. -# sub_heading: "We need your language skills." - pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Hindi but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Hindi." - missing_translations: "Until we can translate everything into Hindi, you'll see English when Hindi isn't available." -# learn_more: "Learn more about being a Diplomat" -# subscribe_as_diplomat: "Subscribe as a Diplomat" + title: "CodeCombat अनुवाद में मदद करें!" # This shows up when a player switches to a non-English language using the language selector. + sub_heading: "हमें आपके भाषा कौशल की जरूरत है|" + pitch_body: "हम CodeCombat को अंग्रेजी में डेवेलोप करते हैं परन्तु हमारे पास दुनिया भर से लोग CodeCombat खेलते हैं| उनमें से कई हिंदी में खेलना चाहते हैं लेकिन नहीं खेल पातें है, इसलिए अगर आप दोनों भाषाओँ को जानते हैं, कृपया राजनयिक बन कर हमारी मदद करें|" + missing_translations: "जब तक हम हिंदी में सब कुछ अनुवाद ना कर सकें आपको अंग्रेजी में ही खेलना पड़ेगा|" + learn_more: "एक राजनयिक होने के बारे में और अधिक जानें" + subscribe_as_diplomat: "एक राजनयिक के रूप में सदस्यता लें" # play: # play_as: "Play As" # Ladder page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" # login: # sign_up: "Create Account" # log_in: "Log In" # logging_in: "Logging In" # log_out: "Log Out" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Loading..." # saving: "Saving..." # sending: "Sending..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # fork: "Fork" # play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/hu.coffee b/app/locale/hu.coffee index d87e401e8..ea28e0371 100644 --- a/app/locale/hu.coffee +++ b/app/locale/hu.coffee @@ -1,16 +1,17 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", translation: home: - slogan: "Tanulj meg nyelven programozni, miközben játszol!" + slogan: "Tanulj meg programozni, miközben játszol!" no_ie: "A CodeCombat nem támogatja az Internet Explorer 8, vagy korábbi verzióit. Bocsi!" # Warning that only shows up in IE8 and older no_mobile: "A CodeCombat nem mobil eszközökre lett tervezve. Valószínűleg nem működik helyesen." # Warning that shows up on mobile devices - play: "Játssz!" # The big play button that just starts playing a level + play: "Játssz!" # The big play button that opens up the campaign view. old_browser: "Hohó, a böngésződ már túl régi ahhoz, hogy a CodeCombat futhasson rajta. Bocsi!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Megpróbálhatod éppen, da valószínűleg nem fog működni.." + ipad_browser: "Rossz hír. CodeCombat nem fut iPadon böngészőben. Jó hír: a hivatalos iPad applikációnk csak az Apple jóváhagyására vár." campaign: "Kampány" for_beginners: "Kezdőknek" -# multiplayer: "Multiplayer" # Not currently shown on home page + multiplayer: "Többjátékos" # Not currently shown on home page for_developers: "Fejlesztőknek" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Vagy töltsd le iPadra" nav: play: "Játék" # The top nav bar entry where players choose which levels to play @@ -18,10 +19,10 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t editor: "Szerkesztő" blog: "Blog" forum: "Fórum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + account: "Fiók" + profile: "Profil" + stats: "Statisztika" + code: "Kód" admin: "Admin" # Only shows up when you are an admin home: "Kezdőlap" contribute: "Segítségnyújtás" @@ -29,7 +30,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t about: "Rólunk" contact: "Kapcsolat" twitter_follow: "Követés" -# teachers: "Teachers" + teachers: "Tanárok" modal: close: "Mégse" @@ -48,132 +49,153 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t play: play_as: "Játssz mint" # Ladder page -# spectate: "Spectate" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + spectate: "Néző" # Ladder page + players: "Játékosok" # Hover over a level on /play + hours_played: "Játékidő" # Hover over a level on /play + items: "Tárgyak" # Tooltip on item shop button from /play + unlock: "Feloldás" # For purchasing items and heroes + confirm: "Megerősítés" + owned: "Megszerzett" # For items you own + locked: "Zárolva" + purchasable: "Megvehetvő" # For a hero you unlocked but haven't purchased + available: "Elérhető" + skills_granted: "Elnyert képességek" # Property documentation details + heroes: "Hősök" # Tooltip on hero shop button from /play + achievements: "Eredmények" # Tooltip on achievement list button from /play + account: "Fiók" # Tooltip on account button from /play + settings: "Beállítások" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "Következő" # Go from choose hero to choose inventory before playing a level + change_hero: "Hős váltás" # Go back from choose inventory to choose hero + choose_inventory: "Felszerelés" + buy_gems: "Vásárolj Drágköveket" + subscription_required: "Előfizetést igényel" + anonymous: "Anonímusz Játékos" level_difficulty: "Nehézség: " campaign_beginner: "Kezdő Kampány" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Válaszd ki a pályát!" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Továbbugorhatsz bármelyik pályára, amit lent látsz. Vagy megbeszélheted a pályát a többiekkel " - adventurer_forum: "a Kalandozók Fórumán" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... amelyben megtanulhatod a programozás varázslatait." - campaign_dev: "Véletlenszerű Nehezebb Pályák" - campaign_dev_description: "... amelyekben kicsit nehezebb dolgokkal nézhetsz szembe." + awaiting_levels_adventurer_prefix: "Minden héten öt új pályát teszünk elérhetővé." # {change} + awaiting_levels_adventurer: "Jelentkezz fel mint Kalandor" + awaiting_levels_adventurer_suffix: "legyél az első aki új pályákon játszik." + adjust_volume: "Hangerő beállítása" campaign_multiplayer: "Multiplayer Arénák" campaign_multiplayer_description: "... amelyekben a kódod felveheti a versenyt más játékosok kódjával" - campaign_player_created: "Játékosok pályái" - campaign_player_created_description: "...melyekben <a href=\"/contribute#artisan\">Művészi Varázsló</a> társaid ellen kűzdhetsz." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Nagyon szépen fejlődsz! Mondd el a szüleidnek, mit tanultál a CodeCombat-ban." + email_invalid: "Érvénytelen email-cím." + form_blurb: "Írd be valamelyik szülőd email-címét, és mi megmutatjuk neki!" + form_label: "Email-cím" + placeholder: "email-cím" + title: "Szép munka, Tanítvány" login: sign_up: "Regisztráció" log_in: "Bejelentkezés" logging_in: "Bejelentkezés" log_out: "Kijelentkezés" - recover: "meglévő fiók visszaállítása" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Elfelejtetted a jelszavad?" + authenticate_gplus: "Csatlakozz G+ profillal" + load_profile: "Töltsd be G+ Profilod" + finishing: "Befejezés" + sign_in_with_facebook: "Jelentkezz be Facebook-kal" + sign_in_with_gplus: "Jelentkezz be G+-szal" + signup_switch: "Hozz létre egy fiókot!" signup: -# create_account_title: "Create Account to Save Progress" - description: "Teljesen ingyenes. Csak néhány dologra lesz szükségünk és már kezdheted is a játékot:" email_announcements: "Szeretnél kapni hírlevelet?" - coppa: "Elmúltál már 13? (Vagy az USA-n kívül élsz?)" - coppa_why: "(Miért?)" creating: "Fiók létrehozása" sign_up: "Regisztráció" log_in: "Belépés meglévő fiókkal" social_signup: "De regisztrálhatsz a Facebook-on vagy a G+:-on keresztül is." required: "Csak akkor mehetsz arra, ha már bejelentkeztél." + login_switch: "Már van fiókod?" recover: recover_account_title: "Meglévő fiók visszaállítása" -# send_password: "Send Recovery Password" -# recovery_sent: "Recovery email sent." + send_password: "Küldj Új Jelszót" + recovery_sent: "Új jelszó elküldve." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Elsődleges" + secondary: "Másodlagos" + armor: "Vért" + accessories: "Kiegészítők" + misc: "Egyveleg" + books: "Könyvek" common: + back: "Vissza" # When used as an action verb, like "Navigate backward" + continue: "Folytasd" # When used as an action verb, like "Continue forward" loading: "Töltés..." saving: "Mentés..." sending: "Küldés..." send: "Küldés indítása" cancel: "Mégse" save: "Mentés" -# publish: "Publish" -# create: "Create" + publish: "Publikálás" + create: "Létrehozás" manual: "Kézi" -# fork: "Fork" - play: "Játék" # When used as an action verb, like "Play next level" + fork: "Villára vesz" + play: "Játszd" # When used as an action verb, like "Play next level" retry: "Próbáld újra!" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + actions: "Lehetőségek" + info: "Infó" + help: "Segítség" + watch: "Követés" + unwatch: "Követés vége" + submit_patch: "Kiegészítés bemutatása" + submit_changes: "Változások véglegesítése" +# save_changes: "Save Changes" -# general: -# and: "and" -# name: "Name" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" -# or: "or" -# subject: "Subject" -# email: "Email" -# password: "Password" -# message: "Message" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + general: + and: "és" + name: "Név" + date: "Dátum" + body: "Test" + version: "Verzió" + pending: "Függőben" + accepted: "Elfogadva" + rejected: "Elutasítva" + withdrawn: "Visszavon" + submitter: "Beküldő" + submitted: "Beküldött" + commit_msg: "Üzenet feladása" + review: "Áttekintés" + version_history: "Verzió történet" + version_history_for: "Verzió története ennek: " + select_changes: "Válassz két lehetőséget alul, hogy lásd a különbséget." + undo_prefix: "Visszavonás" +# undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Mégis" +# redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Aktuális pálya előnézete" + result: "Eredmény" + results: "Eredmények" + description: "Leírás" + or: "vagy" + subject: "Tárgy" + email: "Email" + password: "Jelszó" + message: "Üzenet" + code: "Kód" + ladder: "Ranglétra" + when: "Mikor" + opponent: "Ellenfél" + rank: "Rang" + score: "Pont" + win: "Győzelem" + loss: "Vereség" + tie: "Döntetlen" + easy: "Könnyű" + medium: "Közepes" + hard: "Nehéz" + player: "Játékos" + player_level: "Szint" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Harcos" + ranger: "Távolsági" + wizard: "Varázsló" units: second: "másodperc" @@ -194,252 +216,474 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t play_level: done: "Kész" home: "Kezdőlap" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "Pálya" # Like "Level: Dungeons of Kithgard" + skip: "Ugrás" + game_menu: "Játék Menü" guide: "Segítség" restart: "Előlről" goals: "Célok" -# goal: "Goal" -# running: "Running..." + goal: "Cél" + running: "Futás..." success: "Sikerült!" incomplete: "Hiányos" timed_out: "Kifutottál az időből" -# failing: "Failing" + failing: "Bukás" action_timeline: "Akció - Idővonal" click_to_select: "Kattints egy egységre, hogy kijelöld!" -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "Többjátékos" + control_bar_join_game: "Csatlakozz" + reload: "Újratöltés" reload_title: "Újra kezded mindet?" reload_really: "Biztos vagy benne, hogy előlről szeretnéd kezdeni az egész pályát?" reload_confirm: "Előlről az egészet" + victory: "Győzelem" # victory_title_prefix: "" victory_title_suffix: "Kész" victory_sign_up: "Regisztrálj a friss infókért" victory_sign_up_poke: "Szeretnéd, ha levelet küldenénk neked az újításokról? Regisztrálj ingyen egy fiókot, és nem maradsz le semmiről!" victory_rate_the_level: "Értékeld a pályát: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Következő pálya" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_return_to_ladder: "Vissza a ranglétrához" + victory_play_continue: "Tovább" + victory_saving_progress: "Folyamat mentése" victory_go_home: "Vissza a kezdőoldalra" # Only in old-style levels. victory_review: "Mondd el a véleményedet!" # Only in old-style levels. victory_hour_of_code_done: "Készen vagy?" victory_hour_of_code_done_yes: "Igen, ez volt életem kódja!" + victory_experience_gained: "Szerzett tapasztalat" + victory_gems_gained: "Szerzett Drágakövek" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Útmutató" tome_minion_spells: "Egységeid varázslatai" # Only in old-style levels. tome_read_only_spells: "Csak olvasható varázslatok" # Only in old-style levels. tome_other_units: "Egyéb egységek" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). - tome_select_a_thang: "Válassz ki valakit " + tome_cast_button_run: "Futtatás" + tome_cast_button_running: "Futás..." + tome_cast_button_ran: "Lefutott" + tome_submit_button: "Beküldés" + tome_reload_method: "Eredeti Eljárás újratöltése" # Title text for individual method reload button. + tome_select_method: "Válassz Eljárást" + tome_see_all_methods: "Nézd meg az összes szerkeszthető Eljárást" # Title text for method list selector (shown when there are multiple programmable methods). + tome_select_a_thang: "Válassz egy dolgot " tome_available_spells: "Elérhető varázslatok" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" + tome_your_skills: "Képességeid" + tome_help: "Segítség" + tome_current_method: "Aktuális módszer" + hud_continue_short: "Folytatás" + code_saved: "Kód mentve" + skip_tutorial: "Továbblépés (esc)" + keyboard_shortcuts: "Billentyűparancsok" + loading_ready: "Kész!" + loading_start: "Pálya kezdése" + problem_alert_title: "Javítsd ki a Kódod" + problem_alert_help: "Segítség" time_current: "Most:" -# time_total: "Max:" -# time_goto: "Go to:" + time_total: "Maximum:" + time_goto: "Menj" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Próbáld meg újra!" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." + infinite_loop_reset_level: "Pálya újrakezdés" + infinite_loop_comment_out: "Tegye a kódom kommentárba" + tip_toggle_play: "Játék/Szünet kapcsoló Ctrl+P." + tip_scrub_shortcut: "Ctrl+[ és Ctrl+] visszatekerés és gyors-előre." # {change} tip_guide_exists: "Hasznos információkért kattints az oldal tetején az útmutatóra.." - tip_open_source: "A CodeCombat 100%-osan nyitott forráskódu." -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." + tip_open_source: "A CodeCombat 100%-osan nyitott forráskódú." +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat Béta teszt 2013 Októberétől elérhető." tip_think_solution: "A megoldásra gondolj, ne a problémára!" tip_theory_practice: "Elméletben nincs különbség elmélet és gyakorlat között. A gyakorlatban viszont van. - Yogi Berra" tip_error_free: "Két módon lehet hibátlan programot írni. De csak a harmadik működik. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" + tip_debugging_program: "Ha a hibakeresés az az eljárás amikor eltüntetjük a program hibákat, akkor a programozás biztosan az az eljárás amikor elhelyezzük őket. - Edsger W. Dijkstra" tip_forums: "Irány a fórumok, és mondd el mit gondolsz!!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." + tip_baby_coders: "A jövőben még a bébik is Főmágusok lesznek." + tip_morale_improves: "A töltés addig folytatódik, amíg a morál javul." tip_all_species: "Hisszük, hogy minden fajnak egyenlő lehetőségekkel kell bírnia a programozás megtanulására." # tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " + tip_harry: "Te, Mágus, " tip_great_responsibility: "Nagy kódolási képességgel nagy hibaelhárítási felelősség jár." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." + tip_munchkin: "Ha nem eszed meg a zöldségedet, akkor egy máncskin eljön érted álmodban." tip_binary: "A világon csak 10 féle ember van: azok, akik értik a kettes számrendszert és azok, akik nem.." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" + tip_commitment_yoda: "A programozónak van a legmélyebb elköteleződése és a legélesebb elméje. - Yoda" tip_no_try: "Csináld, vagy ne csináld. Próbálkozás nincs. - Yoda" tip_patience: "Türelmed pedig kell, hogy legyen ifjú Padawan. - Yoda" tip_documented_bug: "A dokumentált programhiba már nem hiba; az már jellegzetesség." tip_impossible: "Mindig lehetetlennek tűnik, amíg meg nem tetted. - Nelson Mandela" tip_talk_is_cheap: "Dumálni könnyű. Mutasd a kódot!. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." -# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Varázsló testreszabása" + tip_first_language: "A legszörnyűbb dolog, amit valaha tanulhatsz, az az első programnyelved. - Alan Kay" + tip_hardware_problem: "Kérdés: Hány programozó kell egy lámpakörte kicseréléséhez? Válasz: Egysem, ez hardware-es hiba." + tip_hofstadters_law: "Hofstadter törvénye: Mindig tovább tart, mint ahogy tervezted. Még akkor is, ha figyelembe vetted Hofstadter törvényét." + tip_premature_optimization: "Minden rossz gyökere a korai optimalizáció. - Donald Knuth" + tip_brute_force: "Ha kérdéses a helyzet, használj nyers erőt. - Ken Thompson" + tip_extrapolation: "Csak két fajta ember létezik. Az egyik, aki extrapolál hiányos adatokból..." + tip_superpower: "A programozás képessége van legközelebb a szuperképességekhez." + tip_control_destiny: "A valódi nyílt forráskódban, jogodban áll irányítani a sorsod. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." + tip_code_never_lies: "A kód sosem hazudik, a kommentek néha. — Ron Jeffries" + tip_reusable_software: "Mielőtt a szoftware újrafelhasználható lesz, előbb használhatónak kell lennie." + tip_optimization_operator: "Minden nyelvben van egy optimalizáló operátor. A legtöbb nyelvben ez a ‘//’" + tip_lines_of_code: "A programírás állapotát mérni a programsorok által olyan, mint a repülő építés állapotát mérni a repülő súlya által. — Bill Gates" + tip_source_code: "Meg akarom változtatni a világot, de nem adnák oda a forráskódját." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" + tip_move_forward: "Bármit is teszel, haladj előre. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "Raktár" + save_load_tab: "Ment/Betölt" + options_tab: "Beállítások" + guide_tab: "Vezérfonal" + guide_video_tutorial: "Bevezető videó" + guide_tips: "Tippek" multiplayer_tab: "Többjátékos" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + auth_tab: "Iratkozz fel!" + inventory_caption: "Szereld fel a hősöd!" + choose_hero_caption: "Válassz hős, nyelvet." + save_load_caption: "... and view history" + options_caption: "Beállítások módosítása" + guide_caption: "Jegyzőkönyv és tippek" + multiplayer_caption: "Játssz a barátaiddal!" + auth_caption: "Mentsd el a fejlődésed." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Ranglétra" + view_other_solutions: "Más Megoldások Megtekintése" # {change} + scores: "Pontok" +# top_players: "Top Players by" + day: "Ma" + week: "A Héten" + all: "Mindenkori" + time: "Idő" + damage_taken: "Kapott Sebzés" + damage_dealt: "Kiosztott Sebzés" + difficulty: "Nehézség" + gold_collected: "Összegyűjtött Arany" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "Felszerelési tárgyak" + equipped_item: "Felvett" + required_purchase_title: "Szükséges" + available_item: "Elérhető" + restricted_title: "Limitált" + should_equip: "(dupla katt, hogy felvedd)" + equipped: "(felvett)" + locked: "(zárt)" + restricted: "(Limitált ezen a szinten)" + equip: "Felvesz" + unequip: "Levesz" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + buy_gems: + few_gems: "Néhány drágakő" + pile_gems: "Halom drágakő" + chest_gems: "Láda drágakő" + purchasing: "Vásárlás..." + declined: "A kártyád elutasításra került" + retrying: "Szerver hiba, újra próbálkozás." + prompt_title: "Nincs elég drágaköved" + prompt_body: "Szeretnél többet?" + prompt_button: "Lépj be a boltba" + recovered: "Az előző drágakő vásárlás helyreállt. Kérlek frissítsd az oldalt." +# price: "x3500 / mo" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + subscribe: + comparison_blurb: "Élesítsd képességeid CodeCombat feliratkozással!" + feature1: "60+ alap pálya, 4 világon át" # {change} + feature2: "7 erőteljes <strong>új hős</strong> egyedi képességekkel!" # {change} + feature3: "30+ bónusz pálya" # {change} + feature4: "<strong>3500 bónusz drágakő</strong> minden hónapban!" + feature5: "Videó oktatóanyagok" + feature6: "Prémium email támogatás" +# feature7: "Private <strong>Clans</strong>" + free: "Ingyenes" + month: "hónap" + subscribe_title: "Feliratkozás" + unsubscribe: "Leiratkozás" + confirm_unsubscribe: "Leiratkozás megerősítése" + never_mind: "Nembaj, akkor is Szeretlek" + thank_you_months_prefix: "Köszönjük hogy támogattál minket az elmúlt" + thank_you_months_suffix: "hónapban." + thank_you: "Köszönjük, hogy támogatod a CodeCombat-ot." + sorry_to_see_you_go: "Sajnáljuk hogy elmész. Kérlek tudasd velünk mit csinálhattunk volna jobban." + unsubscribe_feedback_placeholder: "O, mit tettünk?" + parent_button: "Kérdezd meg a szülődet" + parent_email_description: "Küldünk nekik emailt, hogy vehessenek neked CodeCombat feliratkozást." + parent_email_input_invalid: "Érvénytelen email cím" + parent_email_input_label: "Szülő email címe" + parent_email_input_placeholder: "Írd be szülőd email címét" + parent_email_send: "Email Küldés" + parent_email_sent: "Email elküldve!" + parent_email_title: "Mi a szülőd email címe?" + parents: "Szülőknek" + parents_title: "A gyereke programozni tanul majd." # {change} + parents_blurb1: "A CodeCombattal a gyereke valódi programozási feladatokon keresztül tanul. Egyszerű utasításokkal kezdenek, aztán további témákba is betekintést kapnak." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "Havonta 9,99 USD-ért, minden héten új kihívások elé állítjuk őket és személyre szóló emailes támogatást nyújtanak enkik profi programozók." # {change} + parents_blurb3: "100%-os pénzvisszafizetés garancia: 1-kattintásossal leiratkozhat." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "Havi előfizetés" + subscription_required_to_play: "Ehhez a szinthez elő kell fizetnek." + unlock_help_videos: "Végy előfizetést, hogy feloldd az összes videó oktatóanyagot." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + choose_hero: + choose_hero: "Válassz hőst." + programming_language: "Programnyelv" + programming_language_description: "Melyik programnyelvet akarod használni?" + default: "Alapbeállítás" + experimental: "Kísérleti" + python_blurb: "Egyszerű és mégis hatékony, kezdőknek és szakértőknek is." + javascript_blurb: "Az internet nyelve. (Nem azonos a Javaval.)" + coffeescript_blurb: "Szerethetőbb JavaScript szintaxis." + clojure_blurb: "A modern Lisp." + lua_blurb: "Játék programozó nyelv" + io_blurb: "Egyszerű, de különleges." + status: "Státusz" + hero_type: "Hős típus" + weapons: "Fegyverek" + weapons_warrior: "Kardok - Rövid hatótávolság, mágikus erő nélkül." + weapons_ranger: "Számszeríj, Fegyverek - Nagy hatótávolság, mágikus erő nélkül." + weapons_wizard: "Pálcák és Botok - Nagy hatótávolság és mágikus erő." + attack: "Támadás" # Can also translate as "Attack" + health: "Élet" + speed: "Sebesség" + regeneration: "Gyógyulás" + range: "Hatótáv" # As in "attack or visual range" + blocks: "Blokkolás" # As in "this shield blocks this much damage" + backstab: "Hátbaszúrás" # As in "this dagger does this much backstab damage" + skills: "Képességek" + attack_1: "Sebez" + attack_2: "az alábbi" + attack_3: "fegyver sebzés." + health_1: "Kap" + health_2: "az alábbi" + health_3: "páncél életerő." + speed_1: "Mozog" + speed_2: "méter / másodperc." + available_for_purchase: "Megvehető" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Szükséges pályák:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Csak bizonyos hős játszhatja ezt a pályát." -# options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." -# editor_config: "Editor Config" -# editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" + skill_docs: + writable: "írható" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "csak olvasható" + action_name: "név" + action_cooldown: "Újratöltés" + action_specific_cooldown: "Speciális újratöltés" + action_damage: "Sebzés" + action_range: "Lőtávolság" + action_radius: "Körzet" + action_duration: "Hatásidő" + example: "Példa" + ex: "pl." # Abbreviation of "example" + current_value: "Aktuális érték" + default_value: "Eredeti érték" + parameters: "Paraméterek" + returns: "Visszatérés" + granted_by: "Megkapva" + + save_load: + granularity_saved_games: "Mentve" + granularity_change_history: "Történet" + + options: + general_options: "Általános beállítások" # Check out the Options tab in the Game Menu while playing a level + volume_label: "Hangerő" + music_label: "Zene" + music_description: "Háttérzene ki/bekapcsolása" + editor_config: "Szerkesztő Config" + editor_config_title: "Szerkesztő Beállítások" + editor_config_level_language_label: "Nyelv a szinthez" + editor_config_level_language_description: "Definiáld a programnyelvet ehhez a szinthez." + editor_config_default_language_label: "Programnyelv alapbeállítása" + editor_config_default_language_description: "Definiáld a programnyelvet, amin szeretnél kódolni, amikor új szintet kezdesz." + editor_config_keybindings_label: "Gyors-billentyűk" # editor_config_keybindings_default: "Default (Ace)" # editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." + editor_config_livecompletion_label: "Automatikus kód-kitöltés" + editor_config_livecompletion_description: "Gépelés közben automatikus kód-kitöltés javaslatok megmutatása." + editor_config_invisibles_label: "Láthatatlan karakterek" + editor_config_invisibles_description: "Megjeleníti a láthatatlan karaktereket, úgysmint a szóközt vagy a tabot" + editor_config_indentguides_label: "Bekezdés segítő" + editor_config_indentguides_description: "Vízszintes vonalak megjelenítése a jobb áttekinthetőség érdekében." + editor_config_behaviors_label: "Okos viselkedés" + editor_config_behaviors_description: "Automata zárójel és idézet befejezés." -# about: -# why_codecombat: "Why CodeCombat?" -# why_paragraph_1: "If you want to learn to program, you don't need lessons. You need to write a lot of code and have a great time doing it." -# why_paragraph_2_prefix: "That's what programming is about. It's gotta be fun. Not fun like" -# why_paragraph_2_italic: "yay a badge" + about: + why_codecombat: "CodeCombat, de miért?" + why_paragraph_1: "Ha programozni akarsz tanulni, nem kellenek hozzá tanórák. Csak írnod kell egy csomó kódot és jól érezned magad közben." + why_paragraph_2_prefix: "Erről szól a programozás. Buli lesz. Nem viccelek." + why_paragraph_2_italic: "ezaz, kitüntetés" # why_paragraph_2_center: "but fun like" -# why_paragraph_2_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" + why_paragraph_2_italic_caps: "NE ANYA, BE KELL FEJEZNEM A PÁLYÁT!" # why_paragraph_2_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." # why_paragraph_3: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." # press_title: "Bloggers/Press" # press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + team: "Csapat" + george_title: "Főigazgató" # {change} + george_blurb: "Üzletelő" + scott_title: "Programozó" # {change} + scott_blurb: "Megfontolt" + nick_title: "Programozó" # {change} + nick_blurb: "Motivátor" + michael_title: "Programozó" + michael_blurb: "Rendszer Admin" + matt_title: "Programozó" # {change} + matt_blurb: "Bringás" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat: Információ Tanároknak" + intro_1: "CodeCombat egy online játék, amelyik programozni tanít. A tanulók valódi programnyelven kódolnak." + intro_2: "Előzetes tapasztalat nem szükséges!" + free_title: "Mennyibe kerül?" + cost_china: "CodeCombat Kínában ingyenes az első 5 pályára, aztán $9.99 /hó, hogy elérhető legyen 120+ pálya az exkluzív kínai szervereken." # {change} + free_1: "CodeCombat Basic INGYENES! 70-nél is több pálya, amely minden tudást megad." # {change} + free_2: "A havidíjas előfizetés hozzáférést biztosít az oktató videókhoz és az extra gyakoroló pályákhoz." + teacher_subs_title: "Tanárok ingyenes előfizetést kapnak!" + teacher_subs_1: "Lépjen kapcsolatba velünk," # {change} + teacher_subs_2: "hogy megkapja az ingyenes havi előfizetést." # {change} +# teacher_subs_3: "to set up your subscription." + sub_includes_title: "Mit tartalmaz az előfizetés?" + sub_includes_1: "A 80+ alap pályán kívül az előfizetéssel rendelkező tanulók az alábbi extrákhoz férnek hozzá:" # {change} + sub_includes_2: "50+ gyakorló pálya" # {change} + sub_includes_3: "Oktató videók" + sub_includes_4: "Prémium támogatás emailen" + sub_includes_5: "7 új hős egyedi képességekkel" # {change} + sub_includes_6: "3500 bónusz drágakő minden hónapban" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." + who_for_title: "Kinek való a CodeCombat?" + who_for_1: "A CodeCombat-ot 9 évesnél idősebb tanulóknak ajánljuk. Semmilyen programozási előismeret vagy tapasztalat nem szükséges." + who_for_2: "Úgy terveztük meg a CodeCombat-ot, hogy fiúk és lányok számára is élvezetes legyen." + material_title: "Mennyi anyagot tartalmaz?" + material_china: "Körülbelül 22 órányi játékidő a 120+ előfizetőknek járó pályákon, minden héten további 5 új pályával." # {change} + material_1: "Körülbelül 8 órányi ingyenes tartalom kiegészítve 14 órányi előfizetőknek járó tartalommal, minden héten további 5 új pályával." # {change} +# concepts_title: "What concepts are covered?" + how_much_title: "Mennyibe kerül a havi előfizetés?" +# how_much_1: "A" + how_much_2: "havi előfizetés" + how_much_3: "$9.99, ami bármikor lemondható." + how_much_4: "Ezen kívül kedvezményeket adunk nagyobb csoportok részére:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." + sys_requirements_title: "Rendszer-követelmények" + sys_requirements_1: "Egy modern web-böngésző. Újabb verziójú Chrome, Firefox, vagy Safari, illetve Internet Explorer 9 vagy újabb." + sys_requirements_2: "CodeCombat még nem támogatott iPad-en." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Új verzió mentése" new_major_version: "Új főverzió" + submitting_patch: "Patch beküldése..." cla_prefix: "A módosítások elmentéséhez el kell fogadnod a " cla_url: "CLA" cla_suffix: "tartalmát." cla_agree: "ELFOGADOM" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Lépj kapcsolatba velünk" welcome: "Jó hallani felőled! Az alábbi űrlappal tudsz levelet küldeni nekünk." - contribute_prefix: "Ha szívesen közreműködnél, nézz rá a " - contribute_page: "közreműködők lapjára" - contribute_suffix: "!" forum_prefix: "Ha publikus dologról van szó, megpróbálhatod a " forum_page: "fórumban" forum_suffix: " is." +# faq_prefix: "There's also a" + faq: "GYIK" + subscribe_prefix: "Ha segítségre van szükséged egy pályán, " + subscribe: "vásárolj CodeCombat előfizetést" + subscribe_suffix: "és mi örömmel segítünk. " + subscriber_support: "A CodeCombat előfizetők elsőbbséget élveznek az email ügyfélszolgálatunkon." + screenshot_included: "Képernyőkép (screenshot) mellékelve." + where_reply: "Milyen címre válaszoljunk?" send: "Visszajelzés küldése" contact_candidate: "Vedd fel a kapcsolatot a jelölttel" # Deprecated recruitment_reminder: "Használd ezt az űrlapot, hogy tudasd a jelöltekkel, szívesen fogadnád őket egy interjúra. Ne feledd, CodeCombat felszámítja az első évi fizetés 15%-át. A díj a munkavállaló alkalmazásakor esedékes, és 90 napig visszafizetendő, ha a munkavállaó nem marad alkalmazásban. Részidőben, távmunkára és szerződéssel alkalmazottak után nem kell fizetni, valamint gyakornokok után sem." # Deprecated @@ -450,21 +694,28 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t autosave: "A változtatásokat automatikusan elmentjük." me_tab: "Rólad" picture_tab: "Kép" + delete_account_tab: "Fiók törlése" + wrong_email: "Rossz email-cím" +# wrong_password: "Wrong Password" upload_picture: "Tölts föl egy képet" + delete_this_account: "Fiók végleges törlése" +# god_mode: "God Mode" password_tab: "Jelszó" emails_tab: "Levelek" -# admin: "Admin" + admin: "Adminisztrátor" new_password: "Új jelszó" new_password_verify: "Új jelszó megismétlése" + type_in_email: "Írd be az email-címed a törlés megerősítéséhez" # {change} +# type_in_password: "Also, type in your password." email_subscriptions: "Hírlevél feliratkozások" -# email_subscriptions_none: "No Email Subscriptions." + email_subscriptions_none: "Nem kérek email értesítéseket." email_announcements: "Bejelentések" email_announcements_description: "Szeretnél levelet kapni a legújabb fejlesztéseinkről?" email_notifications: "Értesítések" email_notifications_summary: "CodeCombat tevékenységedre vonatkozó személyre szóló, automatikus értesítések beállításai." email_any_notes: "Bármely megjegyzés" email_any_notes_description: "Minden tevékenységgel kapcsolatos e-mail értesítés letiltása." -# email_news: "News" + email_news: "Hírek" email_recruit_notes: "Álláslehetőségek" email_recruit_notes_description: "Ha igazán jól játszol lehet, hogy felveszzük veled a kapcsolatot és megbeszéljük, hogy szerezzünk-e neked egy (jobb) állást." contributor_emails: "Hozzájárulóknak szóló levelek" @@ -475,26 +726,25 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t error_saving: "Hiba a mentés során" saved: "Változtatások elmentve" password_mismatch: "A jelszavak nem egyeznek." -# password_repeat: "Please repeat your password." + password_repeat: "Kérlek ismételd meg a jelszavadat" job_profile: "Munkaköri leírás" # Rest of this section (the job profile stuff and wizard stuff) is deprecated job_profile_approved: "Munkaköri leírásodat a Codecombat jóváhagyta. Munkaadók mindaddig láthatják, amíg meg nem jelölöd inaktívként, vagy négy hétig, ha addig nem kerül megváltoztatásra." job_profile_explanation: "Szió! Töltsd ki ezt és majd kapcsolatba lépünk veled és keresünk neked egy szoftware fejlesztői állást." sample_profile: "Nézz meg egy mintaprofilt!" view_profile: "Nézd meg a profilodat!" - wizard_tab: "Varázsló" - wizard_color: "Varázslód színe" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" + keyboard_shortcuts: + keyboard_shortcuts: "Billentyűparancsok" + space: "Szünet" + enter: "Enter" +# press_enter: "press enter" + escape: "Kilépés" # shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." + run_code: "Aktuális kód futtatása." + run_real_time: "Futattás valós időben." + continue_script: "Megállított kód folytatása." + skip_scripts: "Kihagyható kódrészletek kihagyása." + toggle_playback: "Játék/Megállítás kapcsoló." # scrub_playback: "Scrub back and forward through time." # single_scrub_playback: "Scrub back and forward through time by a single frame." # scrub_execution: "Scrub through current spell execution." @@ -502,72 +752,136 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # toggle_grid: "Toggle grid overlay." # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." + maximize_editor: "Maximális/Minimális kód szerkesztő." -# community: -# main_title: "CodeCombat Community" + community: + main_title: "CodeCombat Közösség" # introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" + level_editor_prefix: "Használd a CodeCombat" + level_editor_suffix: "-t, hogy szerkessz meglévő-, vagy készíts új pályákat. A felhasználók készítettek már pályákat az osztályiknak, hackathonokra, tanulóiknak és testvéreiknek is. Ha egy új pálya elkészítése túl nagy feladatnak tűnik, kezdhetsz egy általunk készített pálya módosításával is." # thang_editor_prefix: "We call units within the game 'thangs'. Use the" # thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + article_editor_prefix: "Hibát találtál valamelyik dokumentumunkban? Szeretnél útmutatót adni valamelyik alkotásodhoz? Próbáld ki a" + article_editor_suffix: "-t és segíts a CodeCombat játékosoknak, hogy a legtöbbet hozhassák ki a játékkal töltött idejükből." + find_us: "Ezeken az oldalakon is megtalálhatsz minket" + social_blog: "Olvasd a CodeCombat blogot a Sett-en" + social_discource: "Csatlakozz a beszélgetéshez a Discourse forumon" + social_facebook: "Like-old a CodeCombat-et a Facebookon" + social_twitter: "Kövesd a CodeCombat-et a Twitteren" + social_gplus: "Csatlakozz a CodeCombat-hez a Google+ -on" + social_hipchat: "Csevegj velünk a publikus CodeCombat HipChat szobában" + contribute_to_the_project: "Vegyél részt a projektben" -# classes: -# archmage_title: "Archmage" -# archmage_title_description: "(Coder)" -# artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" -# scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" -# diplomat_title_description: "(Translator)" -# ambassador_title: "Ambassador" -# ambassador_title_description: "(Support)" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" -# editor: -# main_title: "CodeCombat Editors" -# article_title: "Article Editor" -# thang_title: "Thang Editor" -# level_title: "Level Editor" -# achievement_title: "Achievement Editor" -# back: "Back" + classes: + archmage_title: "Főmágus" + archmage_title_description: "(Kódoló)" + archmage_summary: "Ha oktatási célú játékokban érdekelt fejlesztő vagy csatlakozz főmágusként a CodeCombat csapatához!" + artisan_title: "Alkotóművész" + artisan_title_description: "(Szint Építő)" + artisan_summary: "Építs és ossz meg szinteket a barátaiddal. Legyél alkotóművész, aki másokat tanít programozni." + adventurer_title: "Kalandor" + adventurer_title_description: "(Játékteszter)" + adventurer_summary: "Szerezd meg az új szinteket, meg a feliratkozással elérhetőeket is egy héttel korábban és ingyen, és segíts a debugging-ban a hivatalos kiadás előtt." + scribe_title: "Írnok" + scribe_title_description: "(Cikk Szerkesztő)" + scribe_summary: "A jó kód jó dokumentációt igényel. Írd, szekeszd és fejleszd a fájlokat, amelyeket milliók használnak a Föld minden pontjáról." + diplomat_title: "Diplomata" + diplomat_title_description: "(Fordító)" + diplomat_summary: "CodeCombat fordítása már több mint 45 nyelvre elkezdődött. Segítsd a fordításoddal te is a munkát." + ambassador_title: "Nagykövet" + ambassador_title_description: "(Támogató)" + ambassador_summary: "Vezesd a fórumozókat és mutass utat a kérdezőknek. A CodeCombatot a Nagykövetek reprezentálják a világban." + + editor: + main_title: "CodeCombat Szerkesztők" + article_title: "Cikk Szerkesztő" + thang_title: "Dolog szerkesztő" + level_title: "Szint Szerkesztő" + achievement_title: "Eredmény szerkesztő" +# poll_title: "Poll Editor" + back: "Vissza" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" +# dungeon: "Dungeon" +# indoor: "Indoor" + desert: "Sivatag" + grassy: "Füves" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Kicsi" + large: "Nagy" + fork_title: "Új Verzió villára vétele" # fork_creating: "Creating Fork..." -# generate_terrain: "Generate Terrain" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" + generate_terrain: "Terület generálása" + more: "Több" + wiki: "Tudásbázis" + live_chat: "Élő cset" + thang_main: "Főoldal" +# thang_spritesheets: "Spritesheets" + thang_colors: "Színek" # level_some_options: "Some Options?" -# level_tab_thangs: "Thangs" -# level_tab_scripts: "Scripts" -# level_tab_settings: "Settings" -# level_tab_components: "Components" -# level_tab_systems: "Systems" -# level_tab_docs: "Documentation" + level_tab_thangs: "Dolgok" + level_tab_scripts: "Kódok" + level_tab_settings: "Beállítások" + level_tab_components: "Komponensek" + level_tab_systems: "Rendszerek" + level_tab_docs: "Dokumentáció" # level_tab_thangs_title: "Current Thangs" -# level_tab_thangs_all: "All" + level_tab_thangs_all: "Mind" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" -# article: -# edit_btn_preview: "Preview" -# edit_article_title: "Edit Article" + article: + edit_btn_preview: "Előnézet" + edit_article_title: "Cikk szerkesztés" + +# polls: +# priority: "Priority" # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -695,7 +999,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # translating_diplomats: "Our Translating Diplomats:" # helpful_ambassadors: "Our Helpful Ambassadors:" -# ladder: + ladder: # please_login: "Please log in first before playing a ladder game." # my_matches: "My Matches" # simulate: "Simulate" @@ -724,67 +1028,99 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." # no_ranked_matches_pre: "No ranked matches for the " # no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." -# choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" -# tutorial_play: "Play Tutorial" + choose_opponent: "Válassz ellenfelet!" + select_your_language: "Válassz nyelvet!" + tutorial_play: "Gyakorlójáték" # tutorial_recommended: "Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" -# tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" -# warmup: "Warmup" + tutorial_skip: "Gyakorlójáték átugrása" + tutorial_not_sure: "Nem érted mi folyik?" + tutorial_play_first: "Játssz egy gyakorlójátékot először!" + simple_ai: "Egyszerű MI" + warmup: "Bemelegítés" # friends_playing: "Friends Playing" # log_in_for_friends: "Log in to play with your friends!" # social_connect_blurb: "Connect and play against your friends!" # invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" + fight: "Harc!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" +# tournament_started: ", started" + tournament_ends: "A torna vége" + tournament_ended: "A torna végetért" + tournament_rules: "A torna szabályai" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + rules: "Szabályok" + winners: "Győztesek" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" + user: + stats: "Statisztika" + singleplayer_title: "Egyjátékos Pályák" + multiplayer_title: "Többjátékos Pályák" # achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" + last_played: "Utoljára játszott" + status: "Státusz" + status_completed: "Befejezett" + status_unfinished: "Folyamatban lévő" # no_singleplayer: "No Singleplayer games played yet." # no_multiplayer: "No Multiplayer games played yet." # no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + favorite_prefix: "A kedvenc nyelv " + favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" + achievements: + last_earned: "Utolsónak megszerzett" + amount_achieved: "Mennyiség" + achievement: "Eredmények" + category_contributor: "Közreműködő" + category_ladder: "Ranglétra" + category_level: "Szint" + category_miscellaneous: "Különleges" + category_levels: "Pályák" # category_undefined: "Uncategorized" # current_xp_prefix: "" -# current_xp_postfix: " in total" + current_xp_postfix: " összesen" # new_xp_prefix: "" -# new_xp_postfix: " earned" + new_xp_postfix: " megszerzett" # left_xp_prefix: "" -# left_xp_infix: " until level " + left_xp_infix: " következő szinthez " # left_xp_postfix: "" -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." + account: + recently_played: "Utoljára játszott" + no_recent_games: "Nem játszottál az utóbbi két hétben." + payments: "Befizetések" + purchased: "Vásárolt" + subscription: "Előfizetés" + invoices: "Számlák" + service_apple: "Apple" +# service_web: "Web" + paid_on: "Fizetve" + service: "Szolgáltatás" + price: "Ár" + gems: "Drágakövek" + active: "Aktív" + subscribed: "Előfizetett" + unsubscribed: "Lemondott" + active_until: "Aktív eddig:" + cost: "Ár" + next_payment: "Következő befizetés" + card: "Bankkártya" + status_unsubscribed_active: "Nincs további előfizetés, így a kártya nem lesz megterhelve, de a már kifizetett fennmaradó időben az előfizetés aktív marad." + status_unsubscribed: "Szerezd meg az új pályákat, hősöket, tárgyakat és bónusz drágaköveket CodeCombat előfizetéssel!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -867,9 +1231,9 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." -# legal: + legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -906,8 +1264,8 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # art_description_prefix: "All common content is available under the" # cc_license_url: "Creative Commons Attribution 4.0 International License" # art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" -# art_music: "Music" -# art_sound: "Sound" + art_music: "Zene" + art_sound: "Hang" # art_artwork: "Artwork" # art_sprites: "Sprites" # art_other: "Any and all other non-code creative works that are made available when creating Levels." @@ -920,23 +1278,23 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # rights_desc: "All rights are reserved for Levels themselves. This includes" # rights_scripts: "Scripts" # rights_unit: "Unit configuration" -# rights_description: "Description" + rights_description: "Leírás" # rights_writings: "Writings" # rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." -# ladder_prizes: + ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. # blurb_1: "These prizes will be awarded according to" # blurb_2: "the tournament rules" # blurb_3: "to the top human and ogre players." # blurb_4: "Two teams means double the prizes!" # blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" + rank: "Rang" + prizes: "Díjak" # total_value: "Total Value" # in_cash: "in cash" # custom_wizard: "Custom CodeCombat Wizard" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Varázsló beállításai" - customize_avatar: "Állítsd be az Avatarod!" - active: "Aktív" - color: "Szín" - group: "Csoport" - clothes: "Öltözetek" -# trim: "Trim" - cloud: "Felhő" - team: "Csapat" - spell: "Varázslat" - boots: "Lábbelik" - hue: "Árnyalat" -# saturation: "Saturation" -# lightness: "Lightness" - account_profile: settings: "Beállítások" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Szerkeszd meg a profilodat" @@ -1016,8 +1358,8 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # basics_looking_for_contracting: "Contracting" # basics_looking_for_internship: "Internship" # basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" + name_header: "Add meg a neved" + name_anonymous: "Névtelen Fejlesztő" # name_help: "Name you want employers to see, like 'Nick Winter'." # short_description_header: "Write a short description of yourself" # short_description_blurb: "Add a tagline to help an employer quickly learn more about you." diff --git a/app/locale/id.coffee b/app/locale/id.coffee index f09fc2691..e1dcd61af 100644 --- a/app/locale/id.coffee +++ b/app/locale/id.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # slogan: "Learn to Code by Playing a Game" no_ie: "Maaf, CodeCombat tidak bisa dijalankan pada Internet Explorer 8 dan sebelumnya" # Warning that only shows up in IE8 and older no_mobile: "Maaf. CodeCombat tidak dibuat untuk perangkat mobile" # Warning that shows up on mobile devices - play: "Play" # The big play button that just starts playing a level + play: "Play" # The big play button that opens up the campaign view. old_browser: "Uh oh, Maaf. Versi broser anda terlalu lama " # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Anda bisa mencoba, tapi mungkin tidak akan berjalan." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." campaign: "Campaign" for_beginners: "Pemula" multiplayer: "Multiplayer" # Not currently shown on home page @@ -14,12 +15,12 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind nav: play: "Levels" # The top nav bar entry where players choose which levels to play - community: "Community" + community: "Komunitas" editor: "Editor" blog: "Blog" forum: "Forum" account: "Akun" - profile: "Profile" + profile: "Profil" stats: "Mulai" code: "Code" admin: "Admin" # Only shows up when you are an admin @@ -32,7 +33,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # teachers: "Teachers" modal: - close: "Close" + close: "Tutup" okay: "Okay" not_found: @@ -41,8 +42,8 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind diplomat_suggestion: # title: "Help translate CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. # sub_heading: "We need your language skills." - pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Indonesian but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Indonesian." - missing_translations: "Until we can translate everything into Indonesian, you'll see English when Indonesian isn't available." + pitch_body: "Kami mengembangkan CodeCombat dalam bahasa Inggris, tapi kami sudah memiliki pemain di seluruh dunia. Banyak dari mereka ingin bermain di Indonesia, tetapi tidak berbicara bahasa Inggris, jadi jika Anda dapat berbicara, silakan mempertimbangkan untuk mendaftar untuk menjadi Diplomat dan membantu menerjemahkan kedua situs CodeCombat dan semua tingkatan ke Indonesia." + missing_translations: "Hingga kami bisa menerjemahkan semuanya ke dalam bahasa Indonesia, Anda akan melihat bahasa Inggris ketika Indonesia belum tersedia." # learn_more: "Learn more about being a Diplomat" # subscribe_as_diplomat: "Subscribe as a Diplomat" @@ -56,62 +57,60 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind confirm: "Konfirmasi" # owned: "Owned" # For items you own locked: "Terkunci" - available: "Trsedia" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased + available: "Tersedia" # skills_granted: "Skills Granted" # Property documentation details heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play account: "Akun" # Tooltip on account button from /play - settings: "Settings" # Tooltip on settings button from /play + settings: "Pengaturan" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" - awaiting_levels_adventurer_prefix:"Kami meliris lima level per minggu" + awaiting_levels_adventurer_prefix: "Kami meliris lima level per minggu" # {change} # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Pilih Level Anda" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" campaign_multiplayer: "Arena Multiplayer" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "Buat Akun" log_in: "Masuk" logging_in: "Logging In" log_out: "Keluar" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Kenapa?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Loading..." # saving: "Saving..." # sending: "Sending..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # fork: "Fork" # play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/it.coffee b/app/locale/it.coffee index 11bb86072..da2534b98 100644 --- a/app/locale/it.coffee +++ b/app/locale/it.coffee @@ -2,15 +2,16 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t home: slogan: "Impara a programmare giocando" no_ie: "CodeCombat non supporta Internet Explorer 8 o browser precedenti. Ci dispiace!" # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat non è stato progettato per dispositivi mobile e potrebbe non funzionare!" # Warning that shows up on mobile devices - play: "Gioca" # The big play button that just starts playing a level - old_browser: "Accidenti, il tuo browser è troppo vecchio per giocare a CodeCombat. Mi spiace!" # Warning that shows up on really old Firefox/Chrome/Safari + no_mobile: "CodeCombat non è stato progettato per dispositivi mobili e potrebbe non funzionare!" # Warning that shows up on mobile devices + play: "Gioca" # The big play button that opens up the campaign view. + old_browser: "Accidenti, il tuo browser è troppo vecchio per giocare a CodeCombat. Ci spiace!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Puoi provare lo stesso, ma probabilmente non funzionerà." + ipad_browser: "Cattiva notizia: CodeCombat non funziona nel browser di iPad. Buona notizia: la nostra app nativa per iPad è in attesa di approvazione da Apple." campaign: "Campagna" for_beginners: "Per Principianti" multiplayer: "Multiplayer" # Not currently shown on home page for_developers: "Per Sviluppatori" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "O scarica per iPad" nav: play: "Livelli" # The top nav bar entry where players choose which levels to play @@ -21,7 +22,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t account: "Account" profile: "Profilo" stats: "Statistiche" -# code: "Code" + code: "Codice" admin: "Amministratore" # Only shows up when you are an admin home: "Pagina iniziale" contribute: "Contribuisci" @@ -56,67 +57,65 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t confirm: "Conferma" owned: "In tuo possesso" # For items you own locked: "Bloccato" + purchasable: "Acquistabile" # For a hero you unlocked but haven't purchased available: "Disponibile" - skills_granted: "Abilità Fornite" # Property documentation details + skills_granted: "Abilità fornite" # Property documentation details heroes: "Eroi" # Tooltip on hero shop button from /play achievements: "Imprese" # Tooltip on achievement list button from /play account: "Account" # Tooltip on account button from /play settings: "Impostazioni" # Tooltip on settings button from /play + poll: "Sondaggio" # Tooltip on poll button from /play next: "Procedi" # Go from choose hero to choose inventory before playing a level - change_hero: "Cambia Eroe" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" - buy_gems: "Compra Gemme" - older_campaigns: "Campagne precedenti" - anonymous: "Giocatore Anonimo" + change_hero: "Cambia eroe" # Go back from choose inventory to choose hero + choose_inventory: "Dotazioni" + buy_gems: "Compra gemme" + subscription_required: "E' richiesta l'iscrizione" + anonymous: "Giocatore anonimo" level_difficulty: "Difficoltà: " campaign_beginner: "Campagne per principianti" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Scegli il tuo livello" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Puoi entrare in qualunque livello qui sotto, o scambiare opinioni su questi livelli sul" - adventurer_forum: "forum degli Avventurieri" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... nelle quali imparerai i trucchi della programmazione." - campaign_dev: "Livelli difficili casuali" - campaign_dev_description: "... nei quali imparerai a usare l'interfaccia facendo qualcosa di un po' più difficile." + awaiting_levels_adventurer_prefix: "Pubblichiamo 5 livelli alla settimana." # {change} + awaiting_levels_adventurer: "Iscriviti come Avventuriero" + awaiting_levels_adventurer_suffix: "per essere tra i primi a provare i nuovi livelli." + adjust_volume: "Regola il volume" campaign_multiplayer: "Arene multigiocatore" campaign_multiplayer_description: "... nelle quali programmi faccia a faccia contro altri giocatori." - campaign_player_created: "Creati dai giocatori" - campaign_player_created_description: "... nei quali affronterai la creatività dei tuoi compagni <a href=\"/contribute#artisan\">Stregoni Artigiani</a>." - campaign_classic_algorithms: "Algoritmi classici" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." - campaign_forest: "Campagna nella Foresta" - campaign_dungeon: "Campagna nelle Segrete" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Stai facendo grandi progressi! Dì ai tuoi genitori quanto hai imparato con CodeCombat." + email_invalid: "Indirizzo email non valido." + form_blurb: "Inserisci l'indirizzo email di un genitore qui sotto e glielo mostreremo!" + form_label: "Indirizzo email" + placeholder: "indirizzo email" + title: "Ottimo lavoro, apprendista" login: sign_up: "Crea account" log_in: "Accedi" -# logging_in: "Logging In" + logging_in: "Accesso in corso" log_out: "Disconnetti" - recover: "Recupera account" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Password dimenticata?" + authenticate_gplus: "Iscriviti con G+" + load_profile: "Carica profilo G+" + finishing: "Completato" + sign_in_with_facebook: "Entra con Facebook" + sign_in_with_gplus: "Entra con G+" + signup_switch: "Vuoi creare un account?" signup: - create_account_title: "Crea un account per salvare le partite" - description: "È gratuito. Servono solo un paio di dettagli e sarai pronto per iniziare:" email_announcements: "Ricevi comunicazioni per email" - coppa: "13+ o non-USA" - coppa_why: "(Perché?)" creating: "Creazione account..." sign_up: "Registrati" log_in: "Accedi con la password" social_signup: "Oppure puoi registrarti con Facebook o Google+:" required: "Effettua l'accesso per proseguire." + login_switch: "Hai già un account?" recover: recover_account_title: "Recupera account" send_password: "Invia password di recupero" -# recovery_sent: "Recovery email sent." + recovery_sent: "Password di recupero inviata." items: primary: "Primario" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t books: "Libri" common: + back: "Indietro" # When used as an action verb, like "Navigate backward" + continue: "Continua" # When used as an action verb, like "Continue forward" loading: "Caricamento in corso..." saving: "Salvataggio in corso..." sending: "Invio in corso..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t fork: "Fork" play: "Gioca" # When used as an action verb, like "Play next level" retry: "Riprova" - watch: "Guarda" -# unwatch: "Unwatch" - submit_patch: "Invia Patch" + actions: "Azioni" + info: "Info" + help: "Aiuto" + watch: "Segui" + unwatch: "Non seguire" + submit_patch: "Invia patch" + submit_changes: "Invia modifiche" + save_changes: "Salva modifiche" general: and: "e" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t date: "Data" body: "Testo" version: "Versione" - commit_msg: "Messaggio del Commit" -# version_history: "Version History" -# version_history_for: "Version History for: " + pending: "In attesa" + accepted: "Accettate" + rejected: "Rifiutate" + withdrawn: "Ritirate" + submitter: "Autore" + submitted: "Data creazione" + commit_msg: "Messaggio di commit" + review: "Revisiona" + version_history: "Cronologia versioni" + version_history_for: "Cronologia versioni per: " + select_changes: "Scegli due versioni per vedere la differenza." + undo_prefix: "Annulla" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Ripristina" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Vedi anteprima del livello attuale" result: "Risultato" results: "Risultati" description: "Descrizione" @@ -161,10 +180,10 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t password: "Password" message: "Messaggio" code: "Codice" -# ladder: "Ladder" + ladder: "Scala" when: "Quando" opponent: "Avversario" -# rank: "Rank" + rank: "Classifica" score: "Punteggio" win: "Vittoria" loss: "Sconfitta" @@ -173,7 +192,10 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t medium: "Medio" hard: "Difficile" player: "Giocatore" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Livello" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Guerriero" + ranger: "Ranger" + wizard: "Mago" units: second: "secondo" @@ -195,7 +217,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t done: "Fatto" home: "Pagina iniziale" # Not used any more, will be removed soon. level: "Livello" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" + skip: "Salta" game_menu: "Menu" guide: "Guida" restart: "Ricomincia" @@ -204,199 +226,327 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t running: "Eseguo..." success: "Successo!" incomplete: "Incompleto" - timed_out: "Tempo Scaduto" -# failing: "Failing" + timed_out: "Tempo scaduto" + failing: "Fallito" action_timeline: "Barra temporale delle azioni" click_to_select: "Clicca un'unità per selezionarla." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" + control_bar_multiplayer: "Multigiocatore" + control_bar_join_game: "Unisciti al gioco" reload: "Ricarica" - reload_title: "Ricarica tutto il codice?" + reload_title: "Ricaricare tutto il codice?" reload_really: "Sei sicuro di voler ricominciare il livello?" reload_confirm: "Ricarica tutto" + victory: "Vittoria" victory_title_prefix: "" victory_title_suffix: " Completato" victory_sign_up: "Registrati per gli aggiornamenti" victory_sign_up_poke: "Vuoi ricevere le ultime novità per email? Crea un account gratuito e ti terremo aggiornato!" victory_rate_the_level: "Vota il livello: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" + victory_return_to_ladder: "Ritorna alla classifica" victory_play_continue: "Continua" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Gioca il prossimo livello" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_saving_progress: "Salvataggio progressi" victory_go_home: "Torna alla pagina iniziale" # Only in old-style levels. victory_review: "Dicci di più!" # Only in old-style levels. victory_hour_of_code_done: "Finito?" - victory_hour_of_code_done_yes: "Si, ho finito con la mia ora di programmazione!" + victory_hour_of_code_done_yes: "Si, ho finito la mia ora di programmazione!" + victory_experience_gained: "Punti XP guadagnati" + victory_gems_gained: "Gemme guadagnate" + victory_new_item: "Nuovo oggetto" + victory_viking_code_school: "Fumi sacri, era un livello duro quello che hai passato! Se non sei già uno sviluppatore, dovresti esserlo. Sei appena stato raccomandato per entrare nella Viking Code School, dove potrai raggiungere il prossimo livello di conoscenza e diventare uno sviluppatore web professionale in 14 settimane." + victory_become_a_viking: "Diventa un Vichingo" guide_title: "Guida" tome_minion_spells: "Incantesimi dei tuoi seguaci" # Only in old-style levels. tome_read_only_spells: "Incantesimi in sola lettura" # Only in old-style levels. tome_other_units: "Altre unità" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_run: "Vai" + tome_cast_button_running: "In esecuzione" + tome_cast_button_ran: "Esegui" + tome_submit_button: "Invia codice" + tome_reload_method: "Ricarica codice originale per questo metodo" # Title text for individual method reload button. + tome_select_method: "Scegli un metodo" + tome_see_all_methods: "Vedi tutti i metodi che puoi modificare" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Seleziona qualcuno per " tome_available_spells: "Incantesimi disponibili" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" + tome_your_skills: "Le tue abilità" + tome_help: "Aiuto" + tome_current_method: "Metodo attuale" hud_continue_short: "Continua" -# code_saved: "Code Saved" + code_saved: "Codice salvato" skip_tutorial: "Salta (esc)" -# keyboard_shortcuts: "Key Shortcuts" + keyboard_shortcuts: "Scorciatoie da tastiera" loading_ready: "Pronto!" -# loading_start: "Start Level" + loading_start: "Inizia livello" problem_alert_title: "Sistema il codice" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." + problem_alert_help: "Aiuto" + time_current: "Tempo:" + time_total: "Max:" + time_goto: "Vai a:" + non_user_code_problem_title: "Impossibile caricare livello" + infinite_loop_title: "Ciclo infinito rilevato" + infinite_loop_description: "Il programma iniziale per costruire il mondo non ha mai finito di girare. O è molto lento o è in un ciclo infinito. O ci potrebbe essere un bug. Puoi provare a lasciarlo girare o farlo ripartire. Se non funziona, per favore avvertici." + check_dev_console: "Puoi anche aprire la console sviluppatore per vedere cosa potrebbe non funzionare." + check_dev_console_link: "(istruzioni)" + infinite_loop_try_again: "Riprova" + infinite_loop_reset_level: "Riavvia livello" + infinite_loop_comment_out: "Commenta il codice" + tip_toggle_play: "Scambia play/pausa con Ctrl+P." + tip_scrub_shortcut: "Usa Ctrl+[ e Ctrl+] per tornare indietro o avanzare rapidamente." + tip_guide_exists: "Clicca sulla Guida, nel menù di gioco (in alto nella pagina), per informazioni utili." + tip_open_source: "CodeCombat è 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat è stato lanciato in beta nell'ottobre 2013." + tip_think_solution: "Pensa alla soluzione, non al problema." + tip_theory_practice: "In teoria, non c'è alcuna differenza tra teoria e pratica. Ma in pratica, c'è. - Yogi Berra" + tip_error_free: "Ci sono due modi di scrivere programmi senza errori; solo il terzo funziona. - Alan Perlis" + tip_debugging_program: "Se il debug è il processo di rimuovere i bug, allora programmare deve essere il processo di crearli. - Edsger W. Dijkstra" + tip_forums: "Visita i nostri forum e facci sapere cosa pensi!" + tip_baby_coders: "Nel futuro, persino i neonati saranno Arcimaghi." # tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." + tip_all_species: "Crediamo che chiunque debba avere le stesse opportunità di imparare a programmare." # tip_reticulating: "Reticulating spines." # tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." + tip_great_responsibility: "Da grandi abilità di programmazione derivano grandi responsabilità." + tip_munchkin: "Se non mangi la tua verdura, un munchkin verrà a cercarti mentre dormi." # tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." # tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" # tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" + tip_patience: "Pazienza devi avere, giovane Padawan. - Yoda" + tip_documented_bug: "Un bug documentato non è bug; è una feature." + tip_impossible: "Sembra sempre impossibile fino a quando non ci si riesce. - Nelson Mandela" + tip_talk_is_cheap: "A parlare sono bravi tutti. Mostrami il codice. - Linus Torvalds" # tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." -# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Personalizza il mago" + tip_hardware_problem: "Quanti programmatori servono per cambiare una lampadina? Nessuno, è un problema di hardware." + tip_hofstadters_law: "Legge di Hofstadter: Ci vuole sempre più del previsto, anche quando tieni conto della legge di Hofstadter." + tip_premature_optimization: "L'ottimizzazione prematura è l'origine di tutti i mali. - Donald Knuth" + tip_brute_force: "Quando sei in dubbio, usa la forza bruta. - Ken Thompson" + tip_extrapolation: "Ci sono soltanto due tipi di persone: quelli che possono estrapolare da dati incompleti..." + tip_superpower: "La programmazione è la cosa che più si avvicina a un superpotere." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" + tip_no_code: "Nessun codice è più veloce di nessun codice." + tip_code_never_lies: "Il codice non mente mai, ma i commenti a volte lo fanno. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." + tip_optimization_operator: "Ogni linguaggio ha un operatore di ottimizzazione. Nella maggior parte dei casi questo operatore è ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" + tip_source_code: "Vorrei cambiare il mondo, ma non mi danno il codice sorgente." + tip_javascript_java: "Java sta a JavaScript come Cane sta a Canestro. - Chris Heilmann" + tip_move_forward: "Qualsiasi cosa tu faccia, vai sempre avanti. - Martin Luther King Jr." + tip_google: "Hai un problema che non riesci a risolvere? Cerca su Google!" + tip_adding_evil: "Aggiungendo un pizzico di malvagità." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" + tip_open_source_contribute: "Puoi aiutare CodeCombat a migliorare!" + tip_recurse: "Iterare e umano, usare la ricorsione è divino. - L. Peter Deutsch" + tip_free_your_mind: "Devi liberarti di tutto questo, Neo. Paura, dubbio, sfiducia. Libera la tua mente. - Morpheus" + tip_strong_opponents: "Anche il più tenace degli avversari ha sempre una debolezza. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventario" - save_load_tab: "Salva/Carico" + save_load_tab: "Salva/Carica" options_tab: "Opzioni" guide_tab: "Guida" + guide_video_tutorial: "Tutorial video" + guide_tips: "Suggerimenti" multiplayer_tab: "Multigiocatore" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" + auth_tab: "Registrati" + inventory_caption: "Equipaggia l'eroe" choose_hero_caption: "Scegli eroe, lingua" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" + save_load_caption: "... e vedi lo storico" + options_caption: "Configura" + guide_caption: "Documenti e suggerimenti" multiplayer_caption: "Gioca con i tuoi amici!" -# auth_caption: "Save your progress." + auth_caption: "Salva i tuoi progressi." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Classifica" + view_other_solutions: "Vedi altre soluzioni" # {change} + scores: "Punteggi" + top_players: "Migliori giocatori per" + day: "Oggi" + week: "Questa settimana" + all: "Sempre" + time: "Tempo" + damage_taken: "Danni subiti" + damage_dealt: "Danni inflitti" + difficulty: "Difficoltà" + gold_collected: "Oro collezionato" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "Equipaggiamento" + equipped_item: "In dotazione" + required_purchase_title: "Necessario" + available_item: "Disponibile" + restricted_title: "Vietato" + should_equip: "(doppio clic per prendere)" + equipped: "(preso)" + locked: "(bloccato)" + restricted: "(vietato in questo livello)" + equip: "Prendi" + unequip: "Posa" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + buy_gems: + few_gems: "Qualche gemma" + pile_gems: "Mucchio di gemme" + chest_gems: "Baule di gemme" + purchasing: "Acquisto..." + declined: "La carta è stata rifiutata" + retrying: "Errore del server, riprovo." + prompt_title: "Non hai abbastanza gemme" + prompt_body: "Ne vuoi comprare altre?" + prompt_button: "Entra nel negozio" + recovered: "Acquisto precedente recuperato. Ricaricare la pagina." + price: "x3500 / mese" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + subscribe: + comparison_blurb: "Aumenta le tue competenze con un abbonamento a CodeCombat!" + feature1: "80+ livelli base in 4 mondi" # {change} + feature2: "7 potenti <strong>nuovi eroi</strong> con capacità uniche!" # {change} + feature3: "50+ livelli bonus" # {change} + feature4: "<strong>3500 gemme bonus</strong> ogni mese!" + feature5: "Video tutorial" + feature6: "Supporto via email premium" + feature7: "Clan <strong>privati</strong>" + free: "Gratis" + month: "mese" + subscribe_title: "Abbonati" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" + parent_email_send: "Invia email" + parent_email_sent: "Email inviata!" +# parent_email_title: "What's your parent's email?" + parents: "Per i genitori" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" + + choose_hero: + choose_hero: "Scegli il tuo eroe" + programming_language: "Linguaggio di programmazione" + programming_language_description: "Che linguaggio vuoi usare?" + default: "Predefinito" + experimental: "Sperimentale" + python_blurb: "Semplice e potente, adatto per principianti ed esperti." + javascript_blurb: "Il linguaggio della rete. (Non è Java.)" + coffeescript_blurb: "Una sintassi JavaScript più elaborata." + clojure_blurb: "Un Lisp moderno." + lua_blurb: "Linguaggio per la programmazione di giochi." + io_blurb: "Semplice ma poco amichevole." + status: "Stato" + hero_type: "Tipo" + weapons: "Armi" + weapons_warrior: "Spade - Ravvicinato, nessuna magia" + weapons_ranger: "Archi, fucili - A distanza, nessuna magia" + weapons_wizard: "Bacchette, bastoni - A distanza, con magie" + attack: "Danni" # Can also translate as "Attack" + health: "Salute" + speed: "Velocità" + regeneration: "Rigenerazione" + range: "campo" # As in "attack or visual range" + blocks: "protegge" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" + skills: "Abilità" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." + speed_1: "Si muove a" + speed_2: "metri al secondo." + available_for_purchase: "In vendita" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Livello da sbloccare:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Solo alcuni eroi possono giocare questo livello." + + skill_docs: + writable: "scrivibile" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "sola lettura" + action_name: "nome" + action_cooldown: "Richiede" + action_specific_cooldown: "Riposo" + action_damage: "Danni" + action_range: "Estensione" + action_radius: "Raggio" + action_duration: "Durata" + example: "Esempio" + ex: "es." # Abbreviation of "example" + current_value: "Valore attuale" + default_value: "Valore predefinito" + parameters: "Parametri" + returns: "Restituisce" + granted_by: "Concesso da" save_load: granularity_saved_games: "Salvato" -# granularity_change_history: "History" + granularity_change_history: "Storico" options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level + general_options: "Opzioni generali" # Check out the Options tab in the Game Menu while playing a level volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." -# editor_config: "Editor Config" -# editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" -# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." + music_label: "Musica" + music_description: "Attiva/disattiva musica di sottofondo." + editor_config: "Editor config" + editor_config_title: "Editor di configurazione" + editor_config_level_language_label: "Linguaggio per questo livello" + editor_config_level_language_description: "Definisci il linguaggio di programmazione per questo singolo livello." + editor_config_default_language_label: "Linguaggio predefinito" + editor_config_default_language_description: "Decidi il linguaggio di programmazione nei nuovi livelli." + editor_config_keybindings_label: "Scorciatoie da tastiera" + editor_config_keybindings_default: "Predefinite (Ace)" + editor_config_keybindings_description: "Aggiunge scorciatoie dagli editor comuni." + editor_config_livecompletion_label: "Auto-completamento immediato" + editor_config_livecompletion_description: "Mostra suggerimenti mentre si scrive." + editor_config_invisibles_label: "Mostra invisibili" + editor_config_invisibles_description: "Mostra caratteri invisibili come spazi e tab." + editor_config_indentguides_label: "Mostra guide di indentazione" + editor_config_indentguides_description: "Mostra righe verticali per incolonnare meglio." + editor_config_behaviors_label: "Comportamento intelligente" + editor_config_behaviors_description: "Auto-completa parentesi e virgolette." about: why_codecombat: "Perché CodeCombat?" @@ -404,42 +554,136 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t # why_paragraph_2_prefix: "That's what programming is about. It's gotta be fun. Not fun like" # why_paragraph_2_italic: "yay a badge" # why_paragraph_2_center: "but fun like" -# why_paragraph_2_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" + why_paragraph_2_italic_caps: "NO MAMMA, DEVO FINIRE IL LIVELLO!" # why_paragraph_2_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." # why_paragraph_3: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." -# press_title: "Bloggers/Press" -# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" -# press_paragraph_1_link: "press packet" -# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" + press_title: "Bloggers/Stampa" + press_paragraph_1_prefix: "Vuoi scrivere qualcosa su di noi? Sentiti libero/a di scaricare e utilizzare tutte le risorse incluse nel nostro" + press_paragraph_1_link: "pacchetto per la stampa" + press_paragraph_1_suffix: ". Tutti i loghi e le immagini possono essere usate senza contattarci direttamente." + team: "Team" + george_title: "Co-fondatore" # george_blurb: "Businesser" -# scott_title: "Programmer" + scott_title: "Programmatore" # {change} # scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + nick_title: "Programmatore" # {change} + nick_blurb: "Guru motivatore" + michael_title: "Programmatore" + michael_blurb: "Amministratore di sistema" + matt_title: "Programmatore" # {change} + matt_blurb: "Ciclista" + cat_title: "Mastro artigiano" + cat_blurb: "Manipolatore dell'aria" + josh_title: "Game Designer" + josh_blurb: "Il pavimento è lava" + jose_title: "Musicista" +# jose_blurb: "Taking Off" + retrostyle_title: "Illustratore" + retrostyle_blurb: "Giochi retrò" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Salva nuova versione" new_major_version: "Nuova versione" + submitting_patch: "Invio modifiche in corso..." cla_prefix: "Per salvare le modifiche, prima devi accettare la nostra " cla_url: "CLA" cla_suffix: "." cla_agree: "ACCETTO" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contatta CodeCombat" welcome: "È bello sentirti! Usa questo modulo per mandarci un'email." - contribute_prefix: "Se sei interessato a contribuire, dai un'occhiata alla nostra " - contribute_page: "pagina Contribuisci" - contribute_suffix: "!" forum_prefix: "Per discussioni pubbliche, puoi provare " forum_page: "il nostro forum" forum_suffix: " invece." + faq_prefix: "C'è anche una" + faq: "FAQ" + subscribe_prefix: "Se hai bisogno di aiuto nel completare un livello, per favore" + subscribe: "acquista un abbonamento a CodeCombat" + subscribe_suffix: "e saremo felici di aiutarti con il tuo codice." + subscriber_support: "Dato che hai un abbonamento a CodeCombat, la tua email avrà supporto prioritario." + screenshot_included: "Screenshot incluso." + where_reply: "Dove dovremmo risponderti?" send: "Invia feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,20 +694,27 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t autosave: "Modifiche salvate automaticamente" me_tab: "Io" picture_tab: "Immagine" + delete_account_tab: "Cancella il tuo account" + wrong_email: "Indirizzo email sbagliato" +# wrong_password: "Wrong Password" upload_picture: "Carica immagine" + delete_this_account: "Cancella questo account per sempre" +# god_mode: "God Mode" password_tab: "Password" emails_tab: "Email" admin: "Amministratore" new_password: "Nuova password" new_password_verify: "Verifica" - email_subscriptions: "Sottoscrizioni email" -# email_subscriptions_none: "No Email Subscriptions." + type_in_email: "Scrivi il tuo indirizzo email per confermare la cancellazione" # {change} +# type_in_password: "Also, type in your password." + email_subscriptions: "Iscrizioni alle email" + email_subscriptions_none: "Nessuna iscrizione." email_announcements: "Annunci email" email_announcements_description: "Ricevi email con le ultime novità e sviluppi di CodeCombat." email_notifications: "Notifiche email" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." + email_notifications_summary: "Controllo per le notifiche automatiche personalizzate sulla tua attività in CodeCombat." + email_any_notes: "Nessuna notifica" + email_any_notes_description: "Blocca tutte le email di notifica." email_news: "Novità" email_recruit_notes: "Lavora con noi" email_recruit_notes_description: "Se sai giocare molto bene potremmo contattarti per offrirti un lavoro (migliore)." @@ -475,100 +726,163 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t error_saving: "Errore durante il salvataggio" saved: "Modifiche salvate" password_mismatch: "La password non corrisponde." -# password_repeat: "Please repeat your password." + password_repeat: "Ripeti la tua password." # job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated # job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - wizard_tab: "Stregone" - wizard_color: "Colore dei vestiti da Stregone" + view_profile: "Visualizza il tuo profilo" keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" + keyboard_shortcuts: "Scorciatoie da tastiera" space: "Spazio" enter: "Invio" +# press_enter: "press enter" escape: "Esc" shift: "Maiusc" -# run_code: "Run current code." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." + run_code: "Esegui codice attuale." + run_real_time: "Esegui in tempo reale." + continue_script: "Continua dopo lo script attuale." + skip_scripts: "Salta tutti gli script saltabili." + toggle_playback: "Gioca/Pausa." + scrub_playback: "Scorri avanti/indietro nel tempo." + single_scrub_playback: "Scorri avanti/indietro di un singolo passo." # scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." + toggle_debug: "Attiva/disattiva schermo debug." + toggle_grid: "Attiva/disattiva griglia." # toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." + beautify: "Rendi ordinato il codice sistemando la formattazione." maximize_editor: "Ingrandisci/rimpicciolisci l'editor di programmazione." -# move_wizard: "Move your Wizard around the level." -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + community: + main_title: "Comunità CodeCombat" + introduction: "Guarda qui sotto in quali modi puoi essere parte del progetto e decidi cosa suona più divertente. Non vediamo l'ora di lavorare con te!" + level_editor_prefix: "Usa il" + level_editor_suffix: "di CodeCombat per creare e modificare livelli. Gli utenti hanno creato livelli per le proprie classi, amici, hackathon, studenti e famiglie. Se credi che creare un livello sia troppo impegnativo puoi cominciare modificando uno dei nostri!" + thang_editor_prefix: "Chiamiamo le unità di gioco 'thang'. Usa il" + thang_editor_suffix: "per modificare le risorse di CodeCombat. Permetti alle unità di lanciare oggetti, altera la direzione di un'animazione, modifica i punti danno di un'unità, oppure carica i tuoi sprite vettoriali." + article_editor_prefix: "Hai trovato un errore in uno dei nostri documenti? Vuoi creare delle istruzioni per una delle tue creazioni? Vai a" + article_editor_suffix: "e aiuta i giocatori di CodeCombat ad ottenere il massimo mentre giocano a programmare." + find_us: "Dove trovarci" + social_blog: "Leggi il blog di CodeCombat su Sett" + social_discource: "Partecipa alle discussioni nel nostro forum Discourse" + social_facebook: "Metti 'mi piace' a CodeCombat su Facebook" + social_twitter: "Segui CodeCombat su Twitter" + social_gplus: "Unisciti a CodeCombat su Google+" + social_hipchat: "Chatta con noi nella HipChat di CodeCombat!" + contribute_to_the_project: "Contribuisci al progetto" + + clans: + clan: "Clan" + clans: "Clan" + new_name: "Nome nuovo clan" + new_description: "Descrizione nuovo clan" + make_private: "Rendi clan privato" + subs_only: "solo per abbonati" + create_clan: "Crea nuovo clan" +# private_preview: "Preview" + public_clans: "Clan pubblici" + my_clans: "I miei clan" + clan_name: "Nome clan" + name: "Nome" + chieftain: "Capoclan" + type: "Tipo" + edit_clan_name: "Modifica nome clan" + edit_clan_description: "Modifica descrizione clan" + edit_name: "modifica nome" + edit_description: "modifica descrizione" + private: "(privato)" + summary: "Riassunto" + average_level: "Livello medio" + average_achievements: "Numero medio di Imprese" + delete_clan: "Elimina clan" + leave_clan: "Lascia clan" + join_clan: "Unisciti al clan" + invite_1: "Invita:" + invite_2: "*Invita dei giocatori ad unirsi a questo clan mandandogli questo link." + members: "Membri" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." + latest_achievement: "Ultima impresa" +# playtime: "Playtime" +# last_played: "Last played" classes: archmage_title: "Arcimago" archmage_title_description: "(Programmazione)" + archmage_summary: "Se sei uno sviluppatore interessato a programmare videogiochi educativi, diventa un Arcimago per aiutarci a costruire CodeCombat!" artisan_title: "Artigiano" artisan_title_description: "(Costruzione livelli)" + artisan_summary: "Crea e condividi livelli con cui far giocare i tuoi amici. Diventa un Artigiano per apprendere l'arte di insegnare agli altri a programmare!" adventurer_title: "Avventuriero" adventurer_title_description: "(Prova di gioco dei livelli)" + adventurer_summary: "Ottieni i nuovi livelli (anche quelli per abbonati) gratuitamente una settimana prima e aiutaci a scovare e sistemare i bug prima della pubblicazione!" scribe_title: "Scriba" scribe_title_description: "(Scrittura articoli)" + scribe_summary: "Del buon codice necessita di buona documentazione. Scrivi, modifica e migliora i documenti letti da milioni di giocatori in tutto il mondo." diplomat_title: "Diplomatico" diplomat_title_description: "(Traduzione)" + diplomat_summary: "CodeCombat è localizzato in più di 45 lingue dai nostri Diplomatici. Aiutaci contribuendo con le tue traduzioni." ambassador_title: "Ambasciatore" ambassador_title_description: "(Supporto)" + ambassador_summary: "Guida gli utenti del forum e dai consigli a coloro che hanno delle domande. I nostri Ambasciatori rappresentano CodeCombat nel mondo." editor: main_title: "Editor di CodeCombat" article_title: "Modifica articolo" thang_title: "Modifica thang" level_title: "Modifica livello" -# achievement_title: "Achievement Editor" -# back: "Back" + achievement_title: "Editor imprese" + poll_title: "Editor di sondaggi" + back: "Indietro" # revert: "Revert" # revert_models: "Revert Models" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" + pick_a_terrain: "Scegli un terreno" + dungeon: "Dungeon" + indoor: "Interno" + desert: "Deserto" + grassy: "Erboso" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Piccolo" + large: "Grande" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." -# generate_terrain: "Generate Terrain" -# more: "More" -# wiki: "Wiki" + generate_terrain: "Genera terreno" + more: "Altro" + wiki: "Wiki" # live_chat: "Live Chat" - level_some_options: "Opzioni??" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" + thang_colors: "Colori" + level_some_options: "Alcune opzioni?" level_tab_thangs: "Thangs" level_tab_scripts: "Script" level_tab_settings: "Impostazioni" level_tab_components: "Componenti" level_tab_systems: "Sistemi" -# level_tab_docs: "Documentation" + level_tab_docs: "Documentazione" level_tab_thangs_title: "Thangs esistenti" -# level_tab_thangs_all: "All" + level_tab_thangs_all: "Tutti" level_tab_thangs_conditions: "Condizioni iniziali" level_tab_thangs_add: "Aggiungi thang" -# delete: "Delete" -# duplicate: "Duplicate" -# rotate: "Rotate" +# level_tab_thangs_search: "Search thangs" + add_components: "Aggiungi Componenti" + component_configs: "Configurazioni componenti" + config_thang: "Doppio click per configurare un thang" + delete: "Cancella" + duplicate: "Duplica" +# stop_duplicate: "Stop Duplicate" + rotate: "Ruota" level_settings_title: "Impostazioni" level_component_tab_title: "Componenti esistenti" level_component_btn_new: "Crea nuovo componente" @@ -579,113 +893,103 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t level_components_type: "Tipo" level_component_edit_title: "Modifica componente" # level_component_config_schema: "Config Schema" -# level_component_settings: "Settings" + level_component_settings: "Impostazioni" level_system_edit_title: "Modifica sistema" create_system_title: "Crea nuovo sistema" new_component_title: "Crea nuovo componente" new_component_field_system: "Sistema" -# new_article_title: "Create a New Article" -# new_thang_title: "Create a New Thang Type" -# new_level_title: "Create a New Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" -# article_search_title: "Search Articles Here" -# thang_search_title: "Search Thang Types Here" -# level_search_title: "Search Levels Here" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." + new_article_title: "Crea un nuovo articolo" + new_thang_title: "Crea un nuovo tipo di Thang" + new_level_title: "Crea un nuovo livello" + new_article_title_login: "Accedi per creare un nuovo articolo" + new_thang_title_login: "Accedi per creare un nuovo tipo di Thang" + new_level_title_login: "Accedi per creare un nuovo livello" + new_achievement_title: "Crea una nuova Impresa" + new_achievement_title_login: "Accedi per creare una nuova Impresa" + new_poll_title: "Crea un nuovo sondaggio" + new_poll_title_login: "Accedi per creare un nuovo sondaggio" + article_search_title: "Cerca articoli qui" + thang_search_title: "Cerca tipi di Thang qui" + level_search_title: "Cerca livelli qui" + achievement_search_title: "Cerca Imprese" + poll_search_title: "Cerca sondaggi" + read_only_warning2: "Nota: non puoi salvare nessuna modifica qui, perchè non hai effettuato l'accesso al sito." + no_achievements: "Nessuna impresa è stata aggiunta a questo livello finora." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" + level_completion: "Completamento livello" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Anteprima" edit_article_title: "Modifica articolo" + polls: + priority: "Priorità" + contribute: page_title: "Contribuire" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" + intro_blurb: "CodeCombat è al 100% open source! Centinaia di giocatori appassionati ci hanno aiutati a rendere il gioco quello che è oggi. Unisciti a noi e scrivi il prossimo capitolo nella missione di CodeCombat per insegnare al mondo a programmare!" + alert_account_message_intro: "Ehilà!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." -# class_attributes: "Class Attributes" + class_attributes: "Attributi di classe" # archmage_attribute_1_pref: "Knowledge in " # archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." # archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" + how_to_join: "Come unirsi" # join_desc_1: "Anyone can help out! Just check out our " # join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " # join_desc_3: ", or find us in our " # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" - more_about_archmage: "Leggi di più su cosa vuol dire diventare un potente Arcimago" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" # artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." # artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" # artisan_join_desc: "Use the Level Editor in these steps, give or take:" -# artisan_join_step1: "Read the documentation." + artisan_join_step1: "Leggi la documentazione." artisan_join_step2: "Crea un nuovo livello ed esplora quelli già esistenti." # artisan_join_step3: "Find us in our public HipChat room for help." - artisan_join_step4: "Posta il tuo livello sul forum per ricevere del feedback." - more_about_artisan: "Leggi di più su cosa vuol dire diventare un creativo Artigiano" + artisan_join_step4: "Pubblica il tuo livello sul forum per sapere cosa ne pensano altre persone." # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" - more_about_adventurer: "Leggi di più su cosa vuol dire diventare un coraggioso Avventuriero" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " scribe_introduction_url_mozilla: "Rete di sviluppo di Mozilla" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." contact_us_url: "Contattaci" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" - more_about_scribe: "Leggi di più su cosa vuol dire diventare un diligente Scrivano" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." diplomat_introduction_pref: "Se c'è una cosa che abbiamo imparato dal " diplomat_launch_url: "lancio di ottobre" diplomat_introduction_suf: "è che c'è un notevole interesse per CodeCombat negli altri paesi, in particolare in Brasile! Stiamo costruendo un corpo di traduttori per trasformare liste di parole in altre parole, per rendere CodeCombat accessibile il più possibile in tutto il mondo. Se ti piace l'idea di sbirciare nei contenuti futuri e di portare questi livelli ai tuoi connazionali il più presto possibile, questa categoria potrebbe essere la tua." diplomat_attribute_1: "Competenza in inglese e nella lingua in cui vorresti tradurre. Per trasferire idee complesse è importante avere una solida capacità in entrambe!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." -# diplomat_join_pref_github: "Find your language locale file " + diplomat_i18n_page_prefix: "Puoi iniziare a tradurre i livelli andando alla nostra" + diplomat_i18n_page: "pagina traduzioni" + diplomat_i18n_page_suffix: ", o sulla nostra interfaccia e sito su GitHub." + diplomat_join_pref_github: "Trova il file della tua lingua " diplomat_github_url: "su GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" - more_about_diplomat: "Leggi di più su cosa vuol dire diventare un grande Diplomatico" + diplomat_join_suf_github: ", modificalo online e invia una richiesta di pull. Ricordati di spuntare la casella qui sotto per essere aggiornato sugli sviluppi delle traduzioni!" diplomat_subscribe_desc: "Ricevi messaggi email sullo sviluppo i18n e i livelli da tradurre." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." ambassador_introduction: "Stiamo costruendo questa comunità, e voi siete i collegamenti. Abbiamo chat Olark, email e reti sociali con tanta gente con cui parlare ed aiutare a familiarizzare con il gioco, e da cui imparare. Se vuoi aiutare le persone a farsi coinvolgere e a divertirsi; se sei entrato nello spirito di CodeCombat e di dove stiamo andando, questa categoria può essere per te." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" + ambassador_join_note_strong: "Nota" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" - more_about_ambassador: "Leggi di più su cosa vuol dire diventare un servizievole Ambasciatore" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." changes_auto_save: "Le modifiche vengono salvate automaticamente quando si segnano le caselle." diligent_scribes: "I nostri diligenti scrivani:" @@ -696,158 +1000,218 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t helpful_ambassadors: "I nostri servizievoli ambasciatori:" ladder: - please_login: "Per favore esegui il log in first prima di giocare una partita classificata ." + please_login: "Per favore accedi prima di giocare una partita classificata." my_matches: "Le mie partite" simulate: "Simula" -# simulation_explanation: "By simulating games you can get your game ranked faster!" -# simulate_games: "Simulate Games!" + simulation_explanation: "Simulando le partite, la tua partita potrà essere classificata più velocemente!" + simulate_games: "Simula partite!" # simulate_all: "RESET AND SIMULATE GAMES" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" + games_simulated_by: "Partite simulate da te:" + games_simulated_for: "Partite simulate per te:" + games_simulated: "Partite simulate" + games_played: "Partite giocate" + ratio: "Rapporto" + leaderboard: "Classifica" # battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " -# summary_wins: " Wins, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" + summary_your: "Le tue " + summary_matches: "Partite - " + summary_wins: " vittorie, " + summary_losses: " sconfitte" + rank_no_code: "Nessun nuovo codice da valutare" + rank_my_game: "Valuta la mia partita!" rank_submitting: "Inviando..." - rank_submitted: "Inviato per essere Valutato" - rank_failed: "Impossibile Valutare" - rank_being_ranked: "Il Gioco è stato Valutato" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" + rank_submitted: "Inviato per essere valutato" + rank_failed: "Impossibile valutare" + rank_being_ranked: "La partita è in valutazione." + rank_last_submitted: "inviata " + help_simulate: "Aiutare a simulare partite?" code_being_simulated: "Il tuo nuovo codice sarà simulato da altri giocatori per essere valutato. Sarà aggiornato ad ogni nuova partita." no_ranked_matches_pre: "Nessuna partita valutata per " no_ranked_matches_post: " squadra! Gioca contro altri avversari e poi torna qui affinchè la tua partita venga valutata." choose_opponent: "Scegli un avversario" -# select_your_language: "Select your language!" + select_your_language: "Scegli il linguaggio!" tutorial_play: "Gioca il Tutorial" tutorial_recommended: "Consigliato se questa è la tua primissima partita" tutorial_skip: "Salta il Tutorial" tutorial_not_sure: "Non sei sicuro di quello che sta accadendo?" tutorial_play_first: "Prima di tutto gioca al Tutorial." -# simple_ai: "Simple AI" -# warmup: "Warmup" + simple_ai: "IA semplice" + warmup: "Allenamento" # friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" + log_in_for_friends: "Accedi per giocare con i tuoi amici!" + social_connect_blurb: "Connettiti e gioca contro i tuoi amici!" + invite_friends_to_battle: "Invita i tuoi amici a unirsi a te nella battaglia!" + fight: "Combatti!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" + tournament_ended: "Torneo concluso" + tournament_rules: "Regole torneo" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + rules: "Regole" + winners: "Vincitori" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + user: + stats: "Statistiche" + singleplayer_title: "Livelli singoli" + multiplayer_title: "Livelli multigiocatore" + achievements_title: "Imprese" + last_played: "Ultimo giocato" + status: "Stato" + status_completed: "Completati" + status_unfinished: "Incompleti" + no_singleplayer: "Nessun livello singolo giocato finora." + no_multiplayer: "Nessun livello multigiocatore giocato finora." + no_achievements: "Nessuna impresa finora." + favorite_prefix: "Linguaggio preferito: " + favorite_postfix: "." + not_member_of_clans: "Non è ancora membro di un clan." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" + achievements: + last_earned: "Ultima compiuta" + amount_achieved: "Quantità" + achievement: "Impresa" # category_contributor: "Contributor" -# category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + category_ladder: "Classifica" + category_level: "Livello" + category_miscellaneous: "Varie" + category_levels: "Livelli" + category_undefined: "Senza categoria" + current_xp_prefix: "" + current_xp_postfix: " in totale" + new_xp_prefix: "" + new_xp_postfix: " guadagnati" + left_xp_prefix: "" + left_xp_infix: " fino al livello " + left_xp_postfix: "" -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." + account: + recently_played: "Giocati di recente" + no_recent_games: "Nessuna partita nelle ultime due settimane." + payments: "Pagamenti" + purchased: "Acquistato" + subscription: "Abbonamento" +# invoices: "Invoices" + service_apple: "Apple" + service_web: "Web" +# paid_on: "Paid On" + service: "Servizio" + price: "Prezzo" + gems: "Gemme" + active: "Attivo" + subscribed: "Abbonato" + unsubscribed: "Non abbonato" + active_until: "Attivo fino al" + cost: "Costo" + next_payment: "Prossimo pagamento" + card: "Carta" + status_unsubscribed_active: "Non sei abbonato e non dovrai pagare, ma per ora il tuo account è attivo." + status_unsubscribed: "Accedi a nuovi livelli, eroi, oggetti e gemme con un abbonamento a CodeCombat!" -# loading_error: -# could_not_load: "Error loading from server" -# connection_failure: "Connection failed." -# unauthorized: "You need to be signed in. Do you have cookies disabled?" -# forbidden: "You do not have the permissions." -# not_found: "Not found." -# not_allowed: "Method not allowed." + account_invoices: + amount: "Valore in dollari statunitensi" + declined: "La tua carta è stata rifiutata." + invalid_amount: "Per favore inserisci un valore in dollari statunitensi." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" + purchasing: "Sto acquistando..." + retrying: "Errore server, riprovo." + success: "Pagato con successo. Grazie!" + + loading_error: + could_not_load: "Errore nel caricamento dal server" + connection_failure: "Connessione fallita." + unauthorized: "Devi avere eseguito l'accesso. Hai i cookie disabilitati?" + forbidden: "Non hai i permessi necessari." + not_found: "Non trovato." + not_allowed: "Metodo non permesso." # timeout: "Server timeout." -# conflict: "Resource conflict." + conflict: "Conflitto di risorse." # bad_input: "Bad input." -# server_error: "Server error." -# unknown: "Unknown error." + server_error: "Errore server." + unknown: "Errore sconosciuto." -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" + resources: + sessions: "Sessioni" + your_sessions: "Le tue sessioni" + level: "Livello" # social_network_apis: "Social Network APIs" # facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" -# facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" -# gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" + facebook_friends: "Amici Facebook" + facebook_friend_sessions: "Sessioni amici Facebook" + gplus_friends: "Amici G+" + gplus_friend_sessions: "Sessioni amici G+" + leaderboard: "Classifica" # user_schema: "User Schema" -# user_profile: "User Profile" + user_profile: "Profilo utente" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" + model: "Modello" + system: "Sistema" + systems: "Sistemi" + component: "Componente" + components: "Componenti" # thang: "Thang" # thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" + level_session: "La tua sessione" + opponent_session: "Sessione avversario" + article: "Articolo" # user_names: "User Names" # thang_names: "Thang Names" # files: "Files" # top_simulators: "Top Simulators" # source_document: "Source Document" -# document: "Document" + document: "Documento" # sprite_sheet: "Sprite Sheet" # employers: "Employers" # candidates: "Candidates" # candidate_sessions: "Candidate Sessions" # user_remark: "User Remark" # user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" + versions: "Versioni" + items: "Oggetti" + hero: "Eroe" + heroes: "Eroi" + achievement: "Impresa" # clas: "CLAs" # play_counts: "Play Counts" -# feedback: "Feedback" + feedback: "Feedback" +# payment_info: "Payment Info" + campaigns: "Campagne" + poll: "Sondaggio" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -865,41 +1229,35 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t multiplayer_hint_label: "Suggerimento:" multiplayer_hint: " Clicca il link per selezionare tutto, quindi premi CMD-C o Ctrl-C per copiare il link." multiplayer_coming_soon: "Ulteriori aggiunte per il multigiocatore in arrivo!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." + multiplayer_sign_in_leaderboard: "Accedi o crea un account per mettere la tua soluzione in classifica." legal: page_title: "Questioni legali" - opensource_intro: "CodeCombat è gratuito da giocare e totalmente open source." + opensource_intro: "CodeCombat è completamente open source." opensource_description_prefix: "Visita il " github_url: "nostro GitHub" - opensource_description_center: "e aiutaci se vuoi! CodeCombat è fatto di molti progetti open-source, e a noi piacciono tutti. Vedi " - archmage_wiki_url: "il nostro wiki degli Arcimaghi" + opensource_description_center: "e aiutaci se vuoi! CodeCombat fa uso di molti progetti open-source, e a noi piacciono tutti. Vedi " + archmage_wiki_url: "la nostra wiki degli Arcimaghi" opensource_description_suffix: "per trovare un elenco dei software che rendono possibile questo gioco." - practices_title: "Buone pratiche di rispetto" - practices_description: "Queste sono le promesse che ti facciamo, come giocatore, in linguaggio un po' meno legale." + practices_title: "Linee guida per il rispetto" + practices_description: "Queste sono le promesse che facciamo a te, il giocatore, in linguaggio un po' meno formale." privacy_title: "Privacy" - privacy_description: "Non venderemo le tue info personali. Intendiamo far soldi eventualmente tramite assunzioni, ma sta' sicuro che non distribuiremo le tue info personali a ditte interessate senza il tuo consenso esplicito." + privacy_description: "Non venderemo nessuna delle tue informazioni personali." security_title: "Sicurezza" - security_description: "Facciamo tutto il possibile per tenere sicure le tue informazioni. Essendo un progetto open source, il nostro sito è aperto liberamente a chiunque per controllare e migliorare i nostri sistemi di sicurezza." + security_description: "Facciamo tutto il possibile per tenere sicure le tue informazioni. Essendo un progetto open source, il nostro sito permette liberamente a chiunque di controllare e migliorare i nostri sistemi di sicurezza." email_title: "Email" email_description_prefix: "Non ti inonderemo di spam. Con le " - email_settings_url: "tue impostazioni di posta" - email_description_suffix: "o con i link contenuti nei messaggi puoi cambiare le tue preferenze o cancellarti facilmente in qualsiasi momento." + email_settings_url: "tue impostazioni email" + email_description_suffix: "o con i link contenuti nei messaggi puoi cambiare le tue preferenze o smettere di riceverli facilmente in qualsiasi momento." cost_title: "Costi" - cost_description: "In questo momento CodeCombat è totalmente gratis! Uno dei nostri obiettivi principali è di mantenerlo così, in modo che più persone possibile ci possano giocare, in qualsiasi condizione. Se le cose si mettessero male, potremmo essere costretti a far pagare l'iscrizione ad alcuni contenuti; ma preferiremmo di no. In ogni caso saremo in grado di sostenere la ditta con:" - recruitment_title: "Assunzioni" - recruitment_description_prefix: "Qui in CodeCombat, diventerai un vero mago - non solo nel gioco, ma anche nella vita reale." - url_hire_programmers: "Nessuno riesce a trovare abbastanza programmatori" - recruitment_description_suffix: "quindi quando avrai perfezionato le tue capacità, se sei d'accordo, invieremo dei campioni dei tuoi migliori risultati di programmazione a qualcuna delle migliaia di ditte che muoiono dalla voglia di assumerti. Ci pagheranno qualcosa, ti pagheranno" - recruitment_description_italic: "tantissimo" - recruitment_description_ending: "il sito resta gratuito e tutti siamo contenti. Ecco il progetto." + cost_description: "Puoi giocare gratuitamente a tutti i livelli principali di CodeCombat, con un abbonamento di 9.99$/mese per accedere a set di livelli extra e ottenere 3500 gemme al mese. Puoi annullare l'abbonamento con un click, e offriamo una garanzia di rimborso completo." copyrights_title: "Diritti e licenze" - contributor_title: "Accordo di licenza per i contributori (CLA)" - contributor_description_prefix: "Tutti i contributi, qui sul sito e sul deposito GitHub, sono soggetti al nostro" + contributor_title: "Accordo di licenza per i contributi (CLA)" + contributor_description_prefix: "Tutti i contributi, qui sul sito e sulla repository GitHub, sono soggetti al nostro" cla_url: "CLA" contributor_description_suffix: "al quale devi dare consenso prima di iniziare a collaborare." code_title: "Codice - MIT" - code_description_prefix: "Tutto il codice posseduto da CodeCombat o posto su codecombat.com, sia sul deposito GitHub che nel database codecombat.com è licenziato con la" + code_description_prefix: "Tutto il codice posseduto da CodeCombat o posto su codecombat.com, sia su GitHub che nel database codecombat.com è licenziato con la" mit_license_url: "licenza MIT" code_description_suffix: "Ciò comprende tutto il codice in Sistemi e Componenti che è reso disponibile da CodeCombat allo scopo di creare nuovi livelli." art_title: "Grafica/musica - Creative Commons" @@ -910,11 +1268,11 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t art_sound: "Suoni" art_artwork: "Grafica" art_sprites: "Sprite" - art_other: "Tutti gli altri lavori creativi di qualsiasi tipo - ma non di codice - resi disponibili durante la creazione dei livelli." - art_access: "Attualmente non c'è un modo semplice e unico di trovare queste risorse. In generale, li puoi trovare usando gli URL come succede nel nostro sito. Oppure contattaci per assistenza, oppure aiutaci ad ampliare il sito per rendere le risorse più facilmente accessibili." - art_paragraph_1: "Per l'attribuzione dei diritti, cita codecombat.com e metti un link nelle vicinanze della risorsa usata o dove è appropriato per l'oggetto in questione." + art_other: "Tutti gli altri lavori creativi di qualsiasi tipo - ma non codice - resi disponibili durante la creazione dei livelli." + art_access: "Attualmente non c'è un modo semplice e unico di trovare queste risorse. In generale, li puoi trovare usando gli URL come fa il nostro sito. Oppure contattaci per assistenza, oppure aiutaci ad ampliare il sito per rendere le risorse più facilmente accessibili." + art_paragraph_1: "Per l'attribuzione dei diritti, cita codecombat.com e metti un link nelle vicinanze della risorsa usata o dove è appropriato per il medium in questione." use_list_1: "Se usato in un video o in un altro gioco, inserire codecombat.com nei crediti." - use_list_2: "Se usato in un sito, inserire un link vicino alla risorsa; ad esempio sotto un'immagine, oppure in una apposita pagina di crediti dove potresti anche menzionare altri lavori CC e programmi OS usati nel sito. Se qualcosa fa già chiaro riferimento a CodeCombat, ad esempio un testo di blog che cita CodeCombat, non è necessario attribuire i crediti separatamente." + use_list_2: "Se usato in un sito, inserire un link vicino alla risorsa; ad esempio sotto un'immagine, oppure in una apposita pagina di crediti dove potresti anche menzionare altri lavori Creative Commons e programmi open source usati nel sito. Se qualcosa fa già chiaro riferimento a CodeCombat, ad esempio un testo di blog che cita CodeCombat, non è necessario attribuire i crediti separatamente." art_paragraph_2: "Se il contenuto utilizzato non è stato creato da CodeCombat ma da un utente di codecombat.com, attribuiscilo a lui e segui le indicazioni dei crediti contenute nella descrizione di quella risorsa (se ci sono)." rights_title: "Diritti riservati" rights_desc: "Per i livelli stessi, tutti i diritti sono riservati. Ciò comprende" @@ -922,58 +1280,42 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t rights_unit: "Configurazione di unità di gioco" rights_description: "Descrizioni" rights_writings: "Testi" - rights_media: "Media (suoni, musica) ed alti contenuti creativi prodotti appositamente per quel livello e non messi a disposizione generale per la creazione dei livelli." + rights_media: "Media (suoni, musica) ed altri contenuti creativi prodotti appositamente per quel livello e non messi a disposizione generale per la creazione dei livelli." rights_clarification: "Per chiarire, qualsiasi cosa sia messa a disposizione nell'Editor livelli allo scopo di creare livelli è in licenza CC, mentre i contenuti creati nell'Editor livelli o inviati nel corso della creazione non lo sono." nutshell_title: "In poche parole" nutshell_description: "Qualsiasi risorsa che inseriamo nell'Editor livelli è di libero uso per la creazione dei livelli. Ci riserviamo però il diritto di limitare la distribuzione dei livelli stessi (creati su codecombat.com) che quindi potranno essere a pagamento in futuro, se questo è ciò che finirà per succedere." - canonical: "La versione inglese di questo documento è quella che fa fede. Se ci sono discrepanze tra le traduzioni, la versione inglese ha la precedenza." + canonical: "La versione inglese di questo documento è quella definitiva a cui fare fede. Se ci sono discrepanze tra le traduzioni, la versione inglese ha la precedenza." -# ladder_prizes: -# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" + ladder_prizes: + title: "Premi torneo" # This section was for an old tournament and doesn't need new translations now. + blurb_1: "Questi premi verranno consegnati in accordo con" + blurb_2: "le regole del torneo" + blurb_3: "ai migliori giocatori 'human' e 'ogre'." + blurb_4: "Due team significa il doppio dei premi!" # blurb_5: "(There will be two first place winners, two second-place winners, etc.)" # rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" + prizes: "Premi" + total_value: "Valore totale" + in_cash: "in denaro" + custom_wizard: "Stregone CodeCombat personalizzato" + custom_avatar: "Avatar CodeCombat personalizzato" + heap: "per sei mesi di accesso \"Startup\"" # credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" + one_month_coupon: "coupon: scegli Rails o HTML" + one_month_discount: "sconto 30%: scegli Rails o HTML" + license: "licenza" # oreilly: "ebook of your choice" - wizard_settings: -# title: "Wizard Settings" - customize_avatar: "Personalizza il tuo personaggio" -# active: "Active" -# color: "Color" -# group: "Group" - clothes: "Abbigliamento" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" - saturation: "Saturazione" - lightness: "Luminosità" - account_profile: settings: "Impostazioni" # We are not actively recruiting right now, so there's no need to add new translations for this section. - edit_profile: "Modifica Profilo" -# done_editing: "Done Editing" - profile_for_prefix: "Profilo di " + edit_profile: "Modifica profilo" + done_editing: "Fine modifica" + profile_for_prefix: "Profilo per " profile_for_suffix: "" # featured: "Featured" # not_featured: "Not Featured" # looking_for: "Looking for:" -# last_updated: "Last updated:" + last_updated: "Ultimo aggiornamento:" # contact: "Contact" # active: "Looking for interview offers now" # inactive: "Not looking for offers right now" @@ -1117,20 +1459,20 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t admin: # av_espionage: "Espionage" # Really not important to translate /admin controls. -# av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" -# av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" + av_espionage_placeholder: "Email o nome utente" + av_usersearch: "Cerca utenti" + av_usersearch_placeholder: "Email, username, nome, qualsiasi cosa" + av_usersearch_search: "Cerca" av_title: "Vista amministratore" av_entities_sub_title: "Entità" av_entities_users_url: "Utenti" av_entities_active_instances_url: "Istanze attive" # av_entities_employer_list_url: "Employer List" # av_entities_candidates_list_url: "Candidate List" -# av_entities_user_code_problems_list_url: "User Code Problems List" + av_entities_user_code_problems_list_url: "Lista problemi codice utenti" av_other_sub_title: "Altro" - av_other_debug_base_url: "Base (for debugging base.jade)" + av_other_debug_base_url: "Base (per il debug di base.jade)" u_title: "Lista utenti" -# ucp_title: "User Code Problems" + ucp_title: "Problemi codice utenti" lg_title: "Ultime partite" -# clas: "CLAs" + clas: "Accordi CLA" diff --git a/app/locale/ja.coffee b/app/locale/ja.coffee index e1b04fe7e..60396f371 100644 --- a/app/locale/ja.coffee +++ b/app/locale/ja.coffee @@ -1,35 +1,36 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", translation: home: - slogan: "ゲームをプレイして学びましょう" + slogan: "ゲームをプレイしてコードを学びましょう" no_ie: "大変申し訳ありませんが、ご利用のブラウザ(IE8以下)はサポートされていません。(ChromeやFirefoxをご利用ください)" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat は携帯端末向けに制作されていないため、動作しない可能性があります。" # Warning that shows up on mobile devices - play: "ゲームスタート" # The big play button that just starts playing a level + play: "ゲーム<br>スタート" # The big play button that opens up the campaign view. old_browser: "ご利用のブラウザはCodeCombatを動作させるには古すぎるようです" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "このまま進めることもできますが、正常動作は保証されません" + ipad_browser: "CodeCombat は、iPad のブラウザでは動作しません。しかし、iPad アプリが現在 Apple の承認待ちとなっています。" campaign: "キャンペーンモード" for_beginners: "初心者向け" multiplayer: "マルチプレイヤー" # Not currently shown on home page for_developers: "開発者向け" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "又はiPadでダウンロード" nav: - play: "ゲームスタート" # The top nav bar entry where players choose which levels to play -# community: "Community" - editor: "レベルエディタ" + play: "ゲームマップへ" # The top nav bar entry where players choose which levels to play + community: "コミュニティー" + editor: "レベルエディター" blog: "ブログ" forum: "掲示板" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + account: "アカウント" + profile: "プロフィール" + stats: "ステータス" + code: "コード" admin: "管理" # Only shows up when you are an admin home: "ホーム" - contribute: "貢献" + contribute: "コントリビュート" legal: "規約" about: "CoCoについて" contact: "お問い合わせ" twitter_follow: "フォロー" -# teachers: "Teachers" + teachers: "教育関係者" modal: close: "閉じる" @@ -41,431 +42,681 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", diplomat_suggestion: title: "CodeCombatを翻訳しましょう!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "あなたの言語力が必要です。" - pitch_body: "CodeCombatは英語で開発されています。日本語でプレイしたい方がたくさんいますが、ゲームの多くはまだ英語のままです。もし、あなたが英語が得意であれば、Diplomat(翻訳者)として登録し、CodeCombatのレベルやサイトの翻訳にご協力ください。" + pitch_body: "CodeCombatは英語で開発されています。日本語でプレイしたい方がたくさんいますが、ゲームの多くはまだ英語のままです。もし、あなたが英語が得意であれば、外交官(翻訳者)として登録し、CodeCombatのレベルやサイトの翻訳にご協力ください。" missing_translations: "翻訳が完了していない部分は、英語で表示されます。" - learn_more: "Diplomat について情報" - subscribe_as_diplomat: "Diplomat登録" + learn_more: "外交官について情報" + subscribe_as_diplomat: "外交官登録" play: -# play_as: "Play As" # Ladder page -# spectate: "Spectate" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + play_as: "としてプレー" # Ladder page + spectate: "観戦" # Ladder page + players: "プレイヤー" # Hover over a level on /play + hours_played: "プレイ時間" # Hover over a level on /play + items: "アイテム" # Tooltip on item shop button from /play + unlock: "アンロック" # For purchasing items and heroes + confirm: "ロックを解除?" + owned: "所有品" # For items you own + locked: "ロック" + purchasable: "購入可能" # For a hero you unlocked but haven't purchased + available: "使用可能" + skills_granted: "追加されるスキル" # Property documentation details + heroes: "ヒーロー" # Tooltip on hero shop button from /play + achievements: "実績" # Tooltip on achievement list button from /play + account: "アカウント" # Tooltip on account button from /play + settings: "設定" # Tooltip on settings button from /play + poll: "投票" # Tooltip on poll button from /play + next: "次へ" # Go from choose hero to choose inventory before playing a level + change_hero: "ヒーローの選択" # Go back from choose inventory to choose hero + choose_inventory: "アイテムを装備" + buy_gems: "ジェムを購入" + subscription_required: "サブスクリプション必須" + anonymous: "名無しのプレイヤー" level_difficulty: "難易度: " campaign_beginner: "初心者のキャンペーン" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "レベル選択" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "別のレベルに移動することができます。レベルについて議論するにはこちら: " - adventurer_forum: "冒険者の掲示板" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "プログラミングの魔法を学びましょう" - campaign_dev: "いろんな難しいレベル" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." + awaiting_levels_adventurer_prefix: "私たちは毎週新しいレベルをリリースします" + awaiting_levels_adventurer: "冒険者として登録すると、" + awaiting_levels_adventurer_suffix: "新たなレベルを最初に遊ぶ事ができます" + adjust_volume: "音量を調整する" campaign_multiplayer: "マルチプレイ・アリーナ" -# campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" + campaign_multiplayer_description: "コーディングで他のプレイヤーに対して格闘しましょう" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "あなたは進歩している! CodeCombatでどんなに学んだかについて、誰かに伝えてください。" + email_invalid: "メールアドレスが無効です" + form_blurb: "下にメールアドレスを入力してください" + form_label: "メールアドレス" + placeholder: "メールアドレス" + title: "良くやった、弟子よ" login: sign_up: "アカウント登録" log_in: "ログイン" logging_in: "ログイン中" log_out: "ログアウト" - recover: "パスワードを忘れた場合はこちら" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "パスワードをお忘れですか?" + authenticate_gplus: "Google+を認証する" + load_profile: "Google+プロフィールをロード" + finishing: "仕上げ中" + sign_in_with_facebook: "Facebookでログイン" + sign_in_with_gplus: "Google+でログイン" + signup_switch: "アカウントを作成しますか?" signup: - create_account_title: "進行状況保存用のアカウント作成" - description: "無料でご登録いただけます。" email_announcements: "メールでお知らせを受け取る" - coppa: "13歳以上または米国以外" - coppa_why: "(COPPAって?)" creating: "アカウントを作成しています..." sign_up: "アカウント登録" log_in: "パスワードでログイン" social_signup: "あるいはFacebookやGoogle+でログイン:" -# required: "You need to log in before you can go that way." + required: "ログインする必要があります" + login_switch: "すでにアカウントをお持ちですか?" recover: recover_account_title: "パスワードを忘れた場合" - send_password: "送信する" -# recovery_sent: "Recovery email sent." + send_password: "パスワードを送信する" + recovery_sent: "復旧メールが送信されました" -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "武器" + secondary: "防具" + armor: "鎧" + accessories: "アクセサリー" + misc: "その他" + books: "書物" common: + back: "戻る" # When used as an action verb, like "Navigate backward" + continue: "次へ" # When used as an action verb, like "Continue forward" loading: "ロード中" - saving: "保存中..." + saving: "セーブ中..." sending: "送信中..." send: "送信" cancel: "キャンセル" - save: "保存" + save: "セーブ" publish: "発行" create: "作成" manual: "手動" -# fork: "Fork" + fork: "分かれ" play: "ゲームスタート" # When used as an action verb, like "Play next level" retry: "リトライ" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + actions: "アクション" + info: "情報" + help: "ヘルプ" + watch: "見る" + unwatch: "見ない" + submit_patch: "パッチを送信" + submit_changes: "変更を送信" + save_changes: "変更を保存" -# general: -# and: "and" -# name: "Name" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" -# or: "or" -# subject: "Subject" -# email: "Email" -# password: "Password" -# message: "Message" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + general: + and: "と" + name: "名前" + date: "日付" + body: "体" + version: "バージョン" + pending: "ペンディング" + accepted: "容認された" + rejected: "拒否された" + withdrawn: "取り下げられました" + submitter: "提出者" + submitted: "提出されました" + commit_msg: "コミットメッセージ" + review: "レビュー" + version_history: "バージョン履歴" + version_history_for: "バージョン履歴:" + select_changes: "違いを見るには以下の二つの変更を選んで下さい" + undo_prefix: "取り消す" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "やり直す" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "現在のレベルのプレビューを再生" + result: "結果" + results: "結果" + description: "説明" + or: "又は" + subject: "件名" + email: "メール" + password: "パスワード" + message: "メッセージ" + code: "コード" + ladder: "ラダー" + when: "いつ" + opponent: "対戦者" + rank: "ランク" + score: "スコア" + win: "勝ち" + loss: "負け" + tie: "引分" + easy: "イージー" + medium: "ミディアム" + hard: "ハード" + player: "プレイヤー" + player_level: "プレイヤーレベル" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "戦士" + ranger: "レンジャー" + wizard: "ウィザード" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "秒" + seconds: "秒" + minute: "分" + minutes: "分" + hour: "時" + hours: "時" + day: "日" + days: "日" + week: "週" + weeks: "週" + month: "月" + months: "月" + year: "年" + years: "年" play_level: done: "完了" home: "ホーム" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "ゲームレベル" # Like "Level: Dungeons of Kithgard" + skip: "スキップ" + game_menu: "ゲームメニュー" guide: "ガイド" restart: "再始動" goals: "目標" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" + goal: "目標" + running: "コンパイル中..." + success: "成功!" + incomplete: "不完全" + timed_out: "時間切れ" + failing: "失敗中" action_timeline: "アクション・タイムライン" click_to_select: "ユニットを左クリックで選択してください" -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "マルチプレイ" + control_bar_join_game: "ゲームに参加する" + reload: "リロード" reload_title: "コードを再読み込ますか?" reload_really: "レベルをリセットします。よろしいですか?" reload_confirm: "リセットする" -# victory_title_prefix: "" + victory: "勝利" + victory_title_prefix: "" victory_title_suffix: "クリア" - victory_sign_up: "進行状況を保存するにはアカウント登録をしてください" - victory_sign_up_poke: "あなたのコードを保存してみませんか? 無料アカウント登録!" + victory_sign_up: "進行状況をセーブするにはアカウント登録をしてください" + victory_sign_up_poke: "あなたのコードをセーブしてみませんか? 無料アカウント登録!" victory_rate_the_level: "このレベルの評価: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "次のレベル" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_return_to_ladder: "ラダーに戻る" + victory_play_continue: "コンテニュー" + victory_saving_progress: "セーブ中" victory_go_home: "ホームに戻る" # Only in old-style levels. victory_review: "フィードバック" # Only in old-style levels. victory_hour_of_code_done: "完了してよろしいですか?" victory_hour_of_code_done_yes: "はい、構いません" + victory_experience_gained: "XP獲得" + victory_gems_gained: "ジェム獲得" + victory_new_item: "ニューアイテム" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "ガイド" tome_minion_spells: "操作できるキャラクターの呪文" # Only in old-style levels. tome_read_only_spells: "読込専用の呪文" # Only in old-style levels. tome_other_units: "その他のユニット" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_run: "実行" + tome_cast_button_running: "実行中" + tome_cast_button_ran: "実行済み" + tome_submit_button: "送信" + tome_reload_method: "このメソッドの元のコードをリロードする" # Title text for individual method reload button. + tome_select_method: "メソッドの選択" + tome_see_all_methods: "編集できるすべてのメソッドをを見る" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "誰かを選択: " tome_available_spells: "利用できる呪文" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_your_skills: "あなたのスキル" + tome_help: "ヘルプ" + tome_current_method: "現在のメソッド" + hud_continue_short: "コンテニュー" + code_saved: "コードがセーブされました" skip_tutorial: "スキップ (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." -# tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." -# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "魔法使いの設定" + keyboard_shortcuts: "キーショートカット" + loading_ready: "準備完了!" + loading_start: "レベルスタート" + problem_alert_title: "コードを修正して下さい" + problem_alert_help: "ヘルプ" + time_current: "今:" + time_total: "最大:" + time_goto: "行く:" + non_user_code_problem_title: "レベルをロードできません" + infinite_loop_title: "無限ループが見つかりました" + infinite_loop_description: "最初のワールドを作るコードが終わりません。単に遅いか、無限ループになっているかでしょう。バグがあるのかもしれません。再試行してみたり、リセットしてデフォルトに戻すこともできます。もし直せないなら私たちに報告してください。" + check_dev_console: "開発者コンソールをみてなにが間違っているか見ることもできます。" + check_dev_console_link: "(説明書)" + infinite_loop_try_again: "再試行する" + infinite_loop_reset_level: "レベルをリセット" + infinite_loop_comment_out: "マイコードをコメントアウト" + tip_toggle_play: "Ctrl+Pで、プレイ/ポーズをトグルする" + tip_scrub_shortcut: "Ctrl+[ と Ctrl+] で巻き戻し、早送りする" # {change} + tip_guide_exists: "ゲームメニュー内のガイド(ページの上部)をクリックし、便利な情報 を見よう。" + tip_open_source: "CodeCombatは、100%オープンソースです!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombatは、2013年10月にベータ版を開始しました。" + tip_think_solution: "問題より、解決策を考えろ。" + tip_theory_practice: "理論的には、理論と実践の間には違いはない。でも実際には、ある。- ヨギ・ベラ" + tip_error_free: "エラーのないプログラムを書くためには二つの方法がある:第三の方法だけ働く。-アラン・パリス" + tip_debugging_program: "デバッグがバグを除去するプロセスならば、プログラミングはバグを書き入れるプロセスだ。- エドガー•ダイクストラ" + tip_forums: "フォーラムで、あなたのご意見をお聞かせください!" + tip_baby_coders: "将来的には、赤ちゃんでもアークメイジになれる。" + tip_morale_improves: "士気が向上するまで、ローディングは続く。" + tip_all_species: "我々は、全ての種族が機会均等のもとでプログラミングを学べることを信じている。" + tip_reticulating: "網状化する背骨" + tip_harry: "君はウィザードだ" + tip_great_responsibility: "偉大なコーディングスキルを持つと、偉大なデバッグ責任も付属してきます。" + tip_munchkin: "野菜を食べなければ、眠ってる間にマンチキンに襲われるぞ。" + tip_binary: "世界には10種類の人がいる:バイナリーを理解する人と、そうでない人。" + tip_commitment_yoda: "プログラマーは、最も深いコミットメントを持つ必要がある, 最も深刻な心。- ヨーダ" + tip_no_try: "やるか、やらないかだ。試すことはない。- ヨーダ" + tip_patience: "忍耐を持つ必要がある、若きパダワンよ。- ヨーダ" + tip_documented_bug: "文書化されたバグはバグではありません。それは仕様です。" + tip_impossible: "何かをやり遂げるまでは、常にそれが不可能に思える。- ネルソン・マンデラ" + tip_talk_is_cheap: "話はもう良い、コードを見せろ。- リーナス・トーバルズ" + tip_first_language: "最初のプログラミング言語を学ぶ時が、最も悲惨な事を学ぶ時だ。- アラン・ケイ" + tip_hardware_problem: "Q:電球を変更するのにプログラマが何人必要ですか? A:一人もいらない、それはハードウェアの問題だ。" + tip_hofstadters_law: "ホフスタッターの法則:いつでも予測以上の時間がかかるものである。ホフスタッターの法則を計算に入れても。" + tip_premature_optimization: "早すぎる最適化は諸悪の根源である。-ドナルド・クヌース" + tip_brute_force: "疑わしい時は、暴力を使え。- ケン・トンプソン" + tip_extrapolation: "人間は二種類に分けることができる。不完全なデータを挿入する者と、しない者だ。" + tip_superpower: "プログラミングは、私達のスーパーパワーに最も近い物" + tip_control_destiny: "本当のオープンソースでは、自分自身の運命をコントロールする権利を持っている。- リーナス・トーバルズ" + tip_no_code: "コードがないプログラムが一番高速である。" + tip_code_never_lies: "コードは決して嘘をつかない、コメントは時々嘘をつく。- ロン・ジェフリーズ" + tip_reusable_software: "ソフトウェアを再利用できる前には、最初に使用可能である事が必要である。" + tip_optimization_operator: "すべての言語は、最適化演算子を持っている。殆どの言語では、その演算子は '//' である。" + tip_lines_of_code: "コードを行数でプログラミングの進捗状況を測定するのは、重量で航空機の設計の進捗状況を測定するみたいな事だ。- ビル・ゲイツ" + tip_source_code: "私は世界を変えたい、でも彼らは私にソースコードを与えてはくれない。" + tip_javascript_java: "カー(Car, 車)は'カー'ペット(Carpet)にあるように Java は「Java」Script にもある。- クリス・ハイルマン" + tip_move_forward: "何をするにしても、前進し続けること。- マーティン・ルーサー・キング・ジュニア" + tip_google: "解決できない問題があるだって?ググっちゃえ!" + tip_adding_evil: "悪を少々加える。" + tip_hate_computers: "コンピュータを憎む人が本当に嫌いなのは下手なプログラマーだ。- ラリー・ニーヴン" + tip_open_source_contribute: "あなたは CodeCombat をより良くすることができます!" + tip_recurse: "繰り返しは人間、再帰は神。 - L・ピーター・ドイツ" + tip_free_your_mind: "全ての雑念を捨てろ、恐怖、疑いも不信も 心を解き放つんだ - モーフィアス" + tip_strong_opponents: "どんな強者にも弱点というものはあるんだ… - うちは イタチ" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "インベントリー" + save_load_tab: "セーブ//ロード" + options_tab: "設定" + guide_tab: "ガイド" + guide_video_tutorial: "チュートリアル動画" + guide_tips: "ヒント" multiplayer_tab: "マルチプレイ" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + auth_tab: "登録する" + inventory_caption: "ヒーローの装備を選ぶ" + choose_hero_caption: "ヒーロー、言語と" + save_load_caption: "...視聴履歴を選択する" + options_caption: "設定を行う" + guide_caption: "ガイドとヒント" + multiplayer_caption: "友達とプレイ!" + auth_caption: "進行状況をセーブする" -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "リーダーボード" + view_other_solutions: "リーダーボードを見る" + scores: "スコア" + top_players: "上位プレイヤー順" + day: "今日" + week: "今週" + all: "オールタイム" + time: "時間" + damage_taken: "受けたダメージ" + damage_dealt: "与えたダメージ" + difficulty: "難易度" + gold_collected: "集めたゴールド" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "アイテムを装備" + equipped_item: "装備済み" + required_purchase_title: "必須品" + available_item: "使用可能" + restricted_title: "制限品" + should_equip: "(ダブルクリックで装備)" + equipped: "(装備済み)" + locked: "(ロック)" + restricted: "(このレベルでは制限品)" + equip: "装備する" + unequip: "装備を外す" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + buy_gems: + few_gems: "少し購入" + pile_gems: "多く購入" + chest_gems: "宝箱を購入" + purchasing: "購入中..." + declined: "カードが拒否されました。" + retrying: "サーバーエラー、再試行中。" + prompt_title: "ジェムが不足" + prompt_body: "もっと購入しますか?" + prompt_button: "ショップに入る" + recovered: "前のジェム購入をリカバリーしました。ページを更新してください。" + price: "x3500 / 月" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + subscribe: + comparison_blurb: "CodeCombatへ課金してスキルを磨きましょう!" + feature1: "80以上の基本レベルが4つの世界に" # {change} + feature2: "7人のパワフルな <strong>ニューヒーロー</strong> とユニークなスキル!" # {change} + feature3: "60以上のボーナスレベル" # {change} + feature4: "<strong>3500のジェム</strong>が毎月ボーナス!" + feature5: "ビデオチュートリアル" + feature6: "プレミアムメールサポート" +# feature7: "Private <strong>Clans</strong>" + free: "無料" + month: "月" + subscribe_title: "課金" + unsubscribe: "無課金" +# confirm_unsubscribe: "Confirm Unsubscribe" + never_mind: "気にしないでください, それでもあなたが好きです" + thank_you_months_prefix: "私達を " + thank_you_months_suffix: "ヶ月サポートしてくださりありがとうございます。" + thank_you: "CodeCombatをサポートして下さりありがとうございます。" +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + choose_hero: + choose_hero: "ヒーロー選択" + programming_language: "プログラミング言語" + programming_language_description: "どのプログラミング言語を使いますか?" + default: "デフォルト" + experimental: "実験的" + python_blurb: "シンプルで強力、初心者や専門家でも使える。" + javascript_blurb: "ウェブの言語。(Java と同じではありません)" + coffeescript_blurb: "より良い JavaScript の構文が使えます。" + clojure_blurb: "現代の Lisp。" + lua_blurb: "ゲーム専用のスクリプト言語。" + io_blurb: "シンプルだがあいまい。" + status: "ステータス" + hero_type: "タイプ" + weapons: "武器" + weapons_warrior: "剣 - 攻撃範囲:短い- 魔法使用不可" + weapons_ranger: "クロスボウ, 銃 - 攻撃範囲:長い- 魔法使用不可" + weapons_wizard: "ワンド、杖- 攻撃範囲:長い- 魔法使用可能" + attack: "攻撃力" # Can also translate as "Attack" + health: "体力" + speed: "移動速度" + regeneration: "自動回復度" + range: "攻撃範囲" # As in "attack or visual range" + blocks: "ブロックパワー" # As in "this shield blocks this much damage" + backstab: "バックスタブ" # As in "this dagger does this much backstab damage" + skills: "スキル" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." + available_for_purchase: "購入可能" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "アンロックレベル:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "特定のヒーローのみ、このレベルをプレイする事が出来ます。" + + skill_docs: + writable: "書き込み可能な" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "リードオンリー" + action_name: "ネーム" + action_cooldown: "テイク" + action_specific_cooldown: "クールダウン" + action_damage: "ダメージ" + action_range: "レンジ" + action_radius: "ラジウス" + action_duration: "デュレーション" + example: "例" + ex: "例" # Abbreviation of "example" + current_value: "現在値" + default_value: "デフォルト値" + parameters: "パラメータ" + returns: "リターン" + granted_by: "スキルを与えてくれるアイテム:" + + save_load: + granularity_saved_games: "セーブされました" + granularity_change_history: "履歴" options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." + general_options: "一般設定" # Check out the Options tab in the Game Menu while playing a level + volume_label: "音量" + music_label: "音楽" + music_description: "BGM をオン/オフ" editor_config: "エディター設定" editor_config_title: "エディターの設定" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" -# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." + editor_config_level_language_label: "このレベルの言語" + editor_config_level_language_description: "このレベルのプログラミング言語を定義する。" + editor_config_default_language_label: "デフォルトプログラミング言語" + editor_config_default_language_description: "新しいレベルの起動時に、書きたいプログラミング言語を設定する。" + editor_config_keybindings_label: "キーバインディング" + editor_config_keybindings_default: "デフォルト(エース)" + editor_config_keybindings_description: "一般的なエディターから知られているショートカットを追加する。" + editor_config_livecompletion_label: "ライブオートコンプリート" + editor_config_livecompletion_description: "コード入力中、オートコンプリートの提案を表示する。" + editor_config_invisibles_label: "編集記号の表示" + editor_config_invisibles_description: "スペースやタブなどの編集記号を表示する。" + editor_config_indentguides_label: "インデントガイドの表示" + editor_config_indentguides_description: "より良いインデントを見るために垂直線を表示する。" + editor_config_behaviors_label: "スマートビヘイビア" + editor_config_behaviors_description: "カッコ、中括弧、および引用符をオートコンプリートする。" -# about: -# why_codecombat: "Why CodeCombat?" -# why_paragraph_1: "If you want to learn to program, you don't need lessons. You need to write a lot of code and have a great time doing it." -# why_paragraph_2_prefix: "That's what programming is about. It's gotta be fun. Not fun like" -# why_paragraph_2_italic: "yay a badge" -# why_paragraph_2_center: "but fun like" -# why_paragraph_2_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" -# why_paragraph_2_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." -# why_paragraph_3: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." -# press_title: "Bloggers/Press" -# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" -# press_paragraph_1_link: "press packet" -# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + about: + why_codecombat: "CodeCombat でプログラミングを学ぶ理由" + why_paragraph_1: "プログラミングを学ぶのに、レッスンは必要はありません。それよりも、より多くのコードを書きながら素晴らしい時間を過ごす事が大事です。" + why_paragraph_2_prefix: "つまり、プログラミングは楽しくなければなりません。しかも、中途半端な楽しさでは駄目です。" + why_paragraph_2_italic: "やった!バッジ獲得!" + why_paragraph_2_center: "のような楽しさではなく、次のような楽しさです:" + why_paragraph_2_italic_caps: "母さん待って、このレベルを終わらせるまで!" + why_paragraph_2_suffix: "CodeCombat はマルチプレイゲームであり、単なるレッスンではありません。私達は貴方が辞めるまで辞めません。" + why_paragraph_3: "ただのゲームにハマるぐらいなら、このゲームにハマってハイテク時代のウィザードになろう!" + press_title: "ブロガー/プレス" + press_paragraph_1_prefix: "このゲームついて書きたいですか?気軽にプレスパケットをダウンロードして、中に含まれている全てのリソースを自由に使って良いです:" + press_paragraph_1_link: "プレスパケットをダウンロード" + press_paragraph_1_suffix: "。全てのロゴやイメージは、私達に連絡することなく使用することができます。" + team: "開発チーム" + george_title: "コーファウンダー" # {change} + george_blurb: "ビジネスの達人" + scott_title: "コーファウンダー" # {change} + scott_blurb: "リーズナブルな奴" + nick_title: "コーファウンダー" # {change} + nick_blurb: "モチベーションの達人" + michael_title: "プログラマー" + michael_blurb: "システム管理者" + matt_title: "プログラマー" # {change} + matt_blurb: "サイクリスト" + cat_title: "チーフアルティザン" + cat_blurb: "エアベンダー" + josh_title: "ゲームデザイナー" + josh_blurb: "床は溶岩" + jose_title: "ミュージック" + jose_blurb: "テークオフ" + retrostyle_title: "イラスト" + retrostyle_blurb: "レトロスタイルのゲーム" + + teachers: + title: "CodeCombat: 教育関係者へのお知らせ" + intro_1: "CodeCombat はプログラミングを教えるオンラインゲームです。生徒は本物のプログラム言語を書きます。" + intro_2: "プログラミングの経験は必要ありません!" + free_title: "価格について" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." + free_1: "CodeCombat は基本的に無料です!80以上のレベルが無料です。" # {change} + free_2: "月々の課金をするとビデオのチュートリアルにアクセスでき、また追加のレベルが楽しめます。" + teacher_subs_title: "教育関係者は無料のサブスクリプションを得ることができます!" + teacher_subs_1: "" # {change} + teacher_subs_2: "に連絡して無料の月々のサブスクリプションを得ましょう。" # {change} +# teacher_subs_3: "to set up your subscription." + sub_includes_title: "サブスクリプションの内容について" + sub_includes_1: "80以上の基本レベルに加えて、生徒は月々のサブスクリプションを得て次の機能が使えます:" # {change} + sub_includes_2: "60以上の練習レベル" # {change} + sub_includes_3: "ビデオチュートリアル" + sub_includes_4: "メールによるサポート" + sub_includes_5: "7人の新しいヒーローとマスターのユニークなスキル" # {change} + sub_includes_6: "3500のジェムが月々支給されます" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." + who_for_title: "CodeCombat が必要なひと" + who_for_1: "私たちは CodeCombat を9歳以上の生徒にオススメしています。プログラミングの経験は必要ありません。" + who_for_2: "私たちは男女問わず遊べるように CodeCombat をデザインしました。" + material_title: "どのぐらいコンテンツがありますか?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." + material_1: "8時間ほどの無料のコンテンツに加え、サブスクリプションによってさらに14時間ほどプレイすることができ、毎週5つの新しいレベルが追加されています。" # {change} + concepts_title: "どのような概念がカバーされているかについて" + how_much_title: "月々のサブスクリプションはいくらですか?" + how_much_1: "" + how_much_2: "月々のサブスクリプションは" + how_much_3: "$9.99 で、これはいつでもキャンセルできます。" + how_much_4: "さらに、私たちは大きなグループにディスカウントを行っています:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." + sys_requirements_title: "動作環境" + sys_requirements_1: "モダンなブラウザ。最新の Chrome や FireFox, Safari など。Internet Explorer 9 以上。" + sys_requirements_2: "CodeCombat はまだ iPad をサポートしていません。" + + teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" + school: "学校名" + location: "市町村" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: - save_version_title: "新しいバージョンを保存" + save_version_title: "新しいバージョンをセーブ" new_major_version: "メジャーバージョンを新しくする" + submitting_patch: "パッチを送信中..." cla_prefix: "変更を適用するには, 私達のCLAに同意する必要があります。" cla_url: "CLA" # cla_suffix: "." cla_agree: "同意する" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "お問い合わせ" welcome: "あなたからの連絡に感謝します。私達にメールを送信するには、このフォームを使ってください。" - contribute_prefix: "もしあなたが開発への貢献に興味がある場合: " - contribute_page: "このページをチェック" - contribute_suffix: "!" forum_prefix: "公開で様々な人と議論したい場合は " forum_page: "こちらのフォーラム" forum_suffix: " でお願いします。" + faq_prefix: "他のサポートもあります:" + faq: "よくある質問(FAQ)" + subscribe_prefix: "レベルをクリアする情報が必要ならば、" + subscribe: "CodeCombatのサブスクリプション" + subscribe_suffix: "を購入頂けると喜んであなたのコードを手伝います。" + subscriber_support: "あなたはCodeCombatにすでに加入しているので、メールは優先サポートされます。" + screenshot_included: "スクリーンショットが含まれています。" + where_reply: "何処へ返信すれば宜しいですか?" send: "フィードバックを送信" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated account_settings: title: "アカウント設定" - not_logged_in: "設定を変更するにはログインまたはアカウント登録してください。" + not_logged_in: "設定を変更するにはログインまたはアカウント登録してください" autosave: "変更は自動的にセーブします" me_tab: "自分" picture_tab: "画像" -# upload_picture: "Upload a picture" + delete_account_tab: "アカウントの削除" + wrong_email: "間違ったメールアドレス" + wrong_password: "間違ったパスワード" + upload_picture: "画像をアップロード" + delete_this_account: "アカウントを完全削除する" + god_mode: "ゴッドモード" password_tab: "パスワード" emails_tab: "メール" admin: "管理者" new_password: "新パスワード" new_password_verify: "新パスワードを再入力" + type_in_email: "アカウントの削除を確認するために、メールアドレスを入力して下さい" + type_in_password: "そして、パスワードを入力してください。" email_subscriptions: "ニュースレターの購読" -# email_subscriptions_none: "No Email Subscriptions." + email_subscriptions_none: "No Email Subscriptions." email_announcements: "お知らせ" email_announcements_description: "CodeCombatの最新のニュースや進展をメールで受け取る" email_notifications: "通知" # email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." # email_any_notes: "Any Notifications" # email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" + email_news: "ニュース" + email_recruit_notes: "求人" # email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." contributor_emails: "開発を手伝ってくれる人向けのメール" contribute_prefix: "私達は開発を手伝ってくれる人を探しています。 詳しくは " @@ -475,26 +726,25 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", error_saving: "セーブ中にエラーが発生しました" saved: "変更しました" password_mismatch: "パスワードが違います" -# password_repeat: "Please repeat your password." + password_repeat: "もう一度パスワードを入力してください" job_profile: "求職情報" # Rest of this section (the job profile stuff and wizard stuff) is deprecated job_profile_approved: "CodeCombatは、あなたの求職情報を承りました。無効にする、もしくは4週間の間変更をしなければ雇用者はあなたの求職情報を見ることができなくなります。" # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - wizard_tab: "魔法使い" - wizard_color: "ウィザードの色" + sample_profile: "サンプルプロファイルを参照してください" + view_profile: "あなたのプロフィールを表示" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." + keyboard_shortcuts: + keyboard_shortcuts: "キーボードショートカット" + space: "スペース" + enter: "エンター" + press_enter: "エンターを押す" + escape: "エスケープ" + shift: "シフト" + run_code: "現在のコードを実行" + run_real_time: "リアルタイムで実行" # continue_script: "Continue past current script." # skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." + toggle_playback: "トグル:プレイ/ポーズ" # scrub_playback: "Scrub back and forward through time." # single_scrub_playback: "Scrub back and forward through time by a single frame." # scrub_execution: "Scrub through current spell execution." @@ -503,58 +753,117 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + community: + main_title: "CodeCombatコミュニティー" + introduction: "あなたが最も楽しいと思う方法で私たちを手伝ってください。私たちはあなたと働くことを楽しみにしています!" + level_editor_prefix: "CodeCombat の" + level_editor_suffix: "を使ってレベルを作成したり編集しましょう。ユーザはクラスのひとや、友人、ハッカソン仲間、学生、兄弟姉妹のためにレベルを作成しています。素晴らしいレベルを作るためにフォークすることから始めましょう。" + thang_editor_prefix: "私たちはゲーム中のユニットを「サング」と呼んでいます。" + thang_editor_suffix: "を使って CodeCombat のアートワークを加工しましょう。ユニットが投射物を投げることを許可したり、アニメーションの方向を変えたり、ユニットのヒットポイントを変えたり、あなたのベクタースプライトをアップロードすることもできます。" + article_editor_prefix: "私たちのドキュメントにミスを見つけましたか?自分の作品のチュートリアルを作りたいですか?" + article_editor_suffix: "を使って CodeCombat のプレイヤーを助けて彼らのプレイタイムを最大限に活用できるようにしましょう。" + find_us: "各サイトで私たちを見る" + social_blog: "Sett の CodeCombat ブログを読む" + social_discource: "Discourse のフォーラムで議論しよう" + social_facebook: "Facebook で CodeCombat にいいね!する" + social_twitter: "Twitter の CodeCombat をフォローする" + social_gplus: "Google+ の CodeCombat に参加する" + social_hipchat: "公開されている CodeCombat の HipChat ルームで私たちとチャットする" + contribute_to_the_project: "プロジェクトに貢献する" + +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" classes: -# archmage_title: "Archmage" - archmage_title_description: "(コーダー)" -# artisan_title: "Artisan" - artisan_title_description: "(レベルの製作者)" - adventurer_title: "Adventurer" + archmage_title: "アークメイジ" + archmage_title_description: "(プログラマ)" + archmage_summary: "もしあなたが教育的なゲームに興味のあるデベロッパーであれば、CodeCombat を作るアーキメイジになれます!" + artisan_title: "アーチザン" + artisan_title_description: "(レベルデザイナー)" + artisan_summary: "レベルを作成し、友達にプレイしてもらおう。アーチザンになって他の人にプログラムを教える術を学ぼう!" + adventurer_title: "冒険者" adventurer_title_description: "(レベルのテストプレイヤー)" -# scribe_title: "Scribe" + adventurer_summary: "我々の新しいレベル(我々のサブスクライバーコンテンツ)を1週間早くゲットして、パブリックリリースの前にバグを見つける手伝いをするのが冒険者です。" + scribe_title: "代書人" scribe_title_description: "(記事の編集者)" -# diplomat_title: "Diplomat" + scribe_summary: "良いコードは良いドキュメントが必要です。書いて、編集して数百万人が読むドキュメントを改善しよう。" + diplomat_title: "外交官" diplomat_title_description: "(翻訳者)" -# ambassador_title: "Ambassador" + diplomat_summary: "CodeCombat は私たちの外交官により45ヶ国語以上の言語にローカライズされています。翻訳に貢献し、私たちを手伝ってください。" + ambassador_title: "大使" ambassador_title_description: "(サポート)" + ambassador_summary: "フォーラムのユーザが投稿する質問に答えます。私たちの大使が CodeCombat を世界に表現します。" editor: main_title: "CodeCombatエディター" # article_title: "Article Editor" -# thang_title: "Thang Editor" -# level_title: "Level Editor" -# achievement_title: "Achievement Editor" -# back: "Back" -# revert: "Revert" -# revert_models: "Revert Models" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." -# generate_terrain: "Generate Terrain" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" + thang_title: "サングエディター" + level_title: "レベルエディター" + achievement_title: "実績エディター" + poll_title: "投票エディター" + back: "バック" + revert: "戻す" + revert_models: "モデルを戻す" + pick_a_terrain: "地形を選択してください" + dungeon: "ダンジョン" + indoor: "屋内" + desert: "砂漠" + grassy: "草原" +# mountain: "Mountain" +# glacier: "Glacier" + small: "小さい" + large: "大きい" + fork_title: "新しいバージョンをフォークする" + fork_creating: "フォークを作成中" + generate_terrain: "地形を生成" + more: "さらに見る" + wiki: "ウィキ" + live_chat: "ライブチャット" + thang_main: "メイン" + thang_spritesheets: "スプライトシート" + thang_colors: "色" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,81 +906,74 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" -# contribute: -# page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." -# class_attributes: "Class Attributes" -# archmage_attribute_1_pref: "Knowledge in " -# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." -# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" -# join_desc_1: "Anyone can help out! Just check out our " -# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " -# join_desc_3: ", or find us in our " -# join_desc_4: "and we'll go from there!" -# join_url_email: "Email us" -# join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" -# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." -# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" -# artisan_introduction_suf: ", then this class might be for you." -# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" -# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." -# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" -# artisan_join_desc: "Use the Level Editor in these steps, give or take:" -# artisan_join_step1: "Read the documentation." -# artisan_join_step2: "Create a new level and explore existing levels." -# artisan_join_step3: "Find us in our public HipChat room for help." -# artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" -# artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" -# adventurer_forum_url: "our forum" -# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" -# adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." + polls: + priority: "プライオリティ" + + contribute: + page_title: "コントリビュート" + intro_blurb: "CodeCombat は100%オープンソースです!何百もの熱心なプレイヤーが私たちがゲームを作るのを手伝っています。私たちと一緒に CodeCombat の次のチャプターを作って世界中のプレイヤーにプログラミングを教えましょう!" + alert_account_message_intro: "やあ、こんにちは!" + alert_account_message: "クラスのメールを購読するには,まずログインが必要です。" + archmage_introduction: "ゲームを作る上で一番重要なのは、たくさんの要素を合成することです。グラフィック、サウンド、リアルタイムネットワーキング、ソーシャルネットワーキング、一般的なプログラミング、ローレベルのデータベースマネジメント、管理画面のデザインやインターフェイスなど多岐に渡ります。やらなくてはいけないことはたくさんあります。もしあなたが経験豊富なプログラマであればアーキメイジになって CodeCombat のコアにコミットしましょう。ぜひとも私たちの最高のプログラミングゲームを手伝ってください。" + class_attributes: "クラスの属性" + archmage_attribute_1_pref: "" + archmage_attribute_1_suf: " の知識か、それを学ぶ欲求。ほとんどの私たちのコードはこの言語で書かれています。もしあなたが Ruby や Python のファンなら親しく感じるでしょう。JavaScript ですが、より素敵なシンタックスです。" + archmage_attribute_2: "プログラミングの経験や、自ら率先して行動すること。私たちは慣れるのをお手伝いしますが、あなたをトレーニングする時間はありません。" + how_to_join: "参加の方法" + join_desc_1: "誰でも" + join_desc_2: "からはじめることができます。また、下のチェックボックをオンにするとアークメイジと CodeCombat の最新情報がメールで届きます。さらに深く一翼を担いたいですか?" + join_desc_3: "をするか、私たちの" + join_desc_4: "で私たちに連絡してください!" + join_url_email: "メール" + join_url_hipchat: "公開の HipChat のルーム" + archmage_subscribe_desc: "コーディングの機会やアナウンスをメールで受け取る" + artisan_introduction_pref: "私たちは、追加のレベルを建設しなければなりません!皆さんはもっとコンテンツを、と叫んでいますが、私達がつくれるのは自分たちの分だけです。今、あなたのワークステーションはレベル1です。私達のレベルエディタをつかえばそんなクリエイターでもギリギリ使えます、そう警戒しないで。あなたがfor-loopにまたがるキャンペーンのビジョンを" + artisan_introduction_suf: "にもっているなら、このクラスはあなたにピッタリです。" + artisan_attribute_1: "Blizzardのレベルエディタなどの構築経験は歓迎しますが、必須ではありません!" + artisan_attribute_2: "全体のテストを何度もすることを願ってます。 よいレベルを作るには 他の人のを真似て見てプレイしてみることが必要です。そしてそこから修正のための多くのものを見つけて準備しましょう。" + artisan_attribute_3: "時間がかかることで, 冒険者と並ぶくらい我慢しなければなりません。 私達のレベルエディターは予備動作が長く使っているとイライラするかもしれません。気をつけてくださいね!" + artisan_join_desc: "レベルエディタを使うために以下のステップを利用してください。" + artisan_join_step1: "ドキュメントを読む" + artisan_join_step2: "新しいレベルを作成し、すでにあるレベルか探す" + artisan_join_step3: "ヘルプが必要なとき公開HipChatルームで私達を探す" + artisan_join_step4: "フィードバックのためフォーラムにあなたのレベルを投稿する" + artisan_subscribe_desc: "レベルエディタアップデートやアナウンスをメールで受け取る" + adventurer_introduction: "あなたの役割をはっきりしましょう。あなたは戦車です。あなたには大きなダメージを負ってもらいます。私たちには新しいレベルを試し、どう改善するか見分けるの役立つ人が必要です。その苦痛は大きなものです。よいゲームを作ることは長い道のりで、最初から正しく動くものなどないのです。もしあなたが耐えることができ、高い生命力を持っているならこのクラスはあなたにピッタリでしょう。" + adventurer_attribute_1: "学習することへの渇き。あなたがコーディングのやり方を学びたいなら私たちはコーディングの方法を教えたいと思っています。おそらくこの授業のほとんどを受けているでしょうけど。" + adventurer_attribute_2: "カリスマ。紳士的であり、改善に必要なことをはっきり表し、改善する方法について提案をします。" + adventurer_join_pref: "アーチザンを獲得(新会員に)し彼らと働き、テストをし新しいレベルがあるときにメールを受信するには以下のチェックボックスをオンにしてください。また、私達はレベルのレビューの投稿をを私達のネットワーク" + adventurer_forum_url: "フォーラム" + adventurer_join_suf: "などで通知する場合はそこでもサインアップをしてください。" + adventurer_subscribe_desc: "新しいレベルをテストするためのメールを受け取る。" # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,25 +984,22 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" -# powerful_archmages: "Our Powerful Archmages:" -# creative_artisans: "Our Creative Artisans:" -# brave_adventurers: "Our Brave Adventurers:" -# translating_diplomats: "Our Translating Diplomats:" -# helpful_ambassadors: "Our Helpful Ambassadors:" + powerful_archmages: "私たちの強力なアークメイジたち:" + creative_artisans: "私たちのクリエイティブなアーチザンたち:" + brave_adventurers: "私たちの勇敢な冒険者たち:" + translating_diplomats: "私たちの翻訳をしてくれる外交官たち:" + helpful_ambassadors: "私たちの有能な大使たち:" -# ladder: + ladder: # please_login: "Please log in first before playing a ladder game." # my_matches: "My Matches" # simulate: "Simulate" @@ -706,17 +1010,17 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # games_simulated_for: "Games simulated for you:" # games_simulated: "Games simulated" # games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" + ratio: "比率 " + leaderboard: "リーダーボード" # battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " -# summary_wins: " Wins, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" + summary_your: "あなたの " + summary_matches: "戦闘数 - " + summary_wins: " 勝利数, " + summary_losses: " 敗北数" + rank_no_code: "新しいコードがランクにありません" # rank_my_game: "Rank My Game!" -# rank_submitting: "Submitting..." -# rank_submitted: "Submitted for Ranking" + rank_submitting: "送信中..." + rank_submitted: "ランキングに送信されました。" # rank_failed: "Failed to Rank" # rank_being_ranked: "Game Being Ranked" # rank_last_submitted: "submitted " @@ -725,44 +1029,47 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # no_ranked_matches_pre: "No ranked matches for the " # no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." # choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" -# tutorial_play: "Play Tutorial" -# tutorial_recommended: "Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" + select_your_language: "使う言語を選んでください!" + tutorial_play: "チュートリアルで遊ぶ" + tutorial_recommended: "はじめて遊ぶ人におすすめ" + tutorial_skip: "チュートリアルをスキップする" # tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" -# warmup: "Warmup" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" + tutorial_play_first: "はじめからチュートリアルを遊ぶ" + simple_ai: "単純なAI" + warmup: "ウォームアップ" + friends_playing: "友達と遊ぶ" + log_in_for_friends: "ログインして友達と遊ぼう" # social_connect_blurb: "Connect and play against your friends!" # invite_friends_to_battle: "Invite your friends to join you in battle!" # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + rules: "ルール" + winners: "勝者" -# user: + user: # stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " + singleplayer_title: "シングルプレイヤーレベル" + multiplayer_title: "マルチプレイヤーレベル" + achievements_title: "アーカイブ" + last_played: "最終プレイ" + status: "ステータス" + status_completed: "コンプリート" + status_unfinished: "未完了" + no_singleplayer: "まだシングルプレイヤーのゲームをプレイしていません。" + no_multiplayer: "まだマルチプレイヤーのゲームをプレイしていません。" + no_achievements: "まだアーカイブはありません。" + favorite_prefix: "お気に入りの言語は " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,24 +1175,51 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" # merge_conflict_with: "MERGE CONFLICT WITH" # no_changes: "No Changes" -# guide: -# temp: "Temp" + guide: + temp: "テンポラリー" multiplayer: multiplayer_title: "マルチプレイ設定" # We'll be changing this around significantly soon. Until then, it's not important to translate. @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "ウィザードの設定" - customize_avatar: "アバターのカスタマイズ" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" @@ -1118,9 +1460,9 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", admin: # av_espionage: "Espionage" # Really not important to translate /admin controls. # av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" + av_usersearch: "ユーザーサーチ" # av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" + av_usersearch_search: "サーチ" av_title: "管理画面" # av_entities_sub_title: "Entities" av_entities_users_url: "ユーザー" @@ -1130,7 +1472,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # av_entities_user_code_problems_list_url: "User Code Problems List" av_other_sub_title: "その他" # av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "User List" + u_title: "ユーザーリスト" # ucp_title: "User Code Problems" lg_title: "最近のゲーム" clas: "CLA" diff --git a/app/locale/ko.coffee b/app/locale/ko.coffee index 44383efbc..2eb9aefc4 100644 --- a/app/locale/ko.coffee +++ b/app/locale/ko.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t slogan: "쉽고 간단한 게임 배우기" no_ie: "죄송하지만 코드컴뱃은 인터넷 익스플로러 8에서는 동작하지 않습니다." # Warning that only shows up in IE8 and older no_mobile: "코드 컴뱃은 모바일 기기용으로 제작되지 않았습니다. 아마 동작하지 않을 가능성이 높습니다." # Warning that shows up on mobile devices - play: "시작" # The big play button that just starts playing a level + play: "시작" # The big play button that opens up the campaign view. old_browser: "브라우저가 너무 오래된 버전이라 코드 컴뱃을 실행할 수 없습니다." # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "시도해볼 수는 있겠지만..안될 수도 있습니다." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." campaign: "캠페인" for_beginners: "초보자용" multiplayer: "멀티플레이어" # Not currently shown on home page @@ -19,17 +20,17 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t blog: "블로그" forum: "포럼" account: "계정" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + profile: "프로필" + stats: "스탯" + code: "코드" admin: "관리자" # Only shows up when you are an admin home: "홈" contribute: "참여하기" legal: "법" about: "소개" contact: "문의" - twitter_follow: "Follow" -# teachers: "Teachers" + twitter_follow: "팔로우" + teachers: "선생님들" modal: close: "닫기" @@ -49,84 +50,84 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t play: play_as: "Play As " # Ladder page spectate: "관중모드" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + players: "플레이어" # Hover over a level on /play + hours_played: "플레이한 시간" # Hover over a level on /play + items: "아이템" # Tooltip on item shop button from /play + unlock: "해제" # For purchasing items and heroes + confirm: "확인" + owned: "소지함" # For items you own + locked: "잠김" + purchasable: "구매 가능" # For a hero you unlocked but haven't purchased + available: "가능" + skills_granted: "부여된 스킬" # Property documentation details + heroes: "영웅들" # Tooltip on hero shop button from /play + achievements: "성취한 목표" # Tooltip on achievement list button from /play + account: "계정" # Tooltip on account button from /play + settings: "설정" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "다음" # Go from choose hero to choose inventory before playing a level + change_hero: "영웅 교체" # Go back from choose inventory to choose hero + choose_inventory: "장착된 아이템" + buy_gems: "젬 구매" +# subscription_required: "Subscription Required" + anonymous: "이름없는 플레이어" level_difficulty: "난이도: " campaign_beginner: "초보자 캠페인" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "레벨을 선택하세요." # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "아래에 있는 아무 레벨이나 바로 시작하실 수 있습니다. 또는 포럼에서 레벨에 관해 토론하세요 :" - adventurer_forum: "모험가들의 포럼" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... 이곳에서 당신은 프로그래밍의 마법을 배우게 될 것입니다." - campaign_dev: "상급 레벨 랜덤 선택" - campaign_dev_description: "... 이곳에서 당신은 조금 더 어려운 레벨에 도전할때 필요한 조작 방법을 배울 것입니다." + awaiting_levels_adventurer_prefix: "매주 마다 새로운 레벨이 생깁니다." + awaiting_levels_adventurer: "모험자로 등록 하세요!" + awaiting_levels_adventurer_suffix: "새로운 레벨을 가장 먼저 체험하세요!" + adjust_volume: "소리 조절" campaign_multiplayer: "멀티 플레이어 전투장" campaign_multiplayer_description: "... 이곳에서 당신은 다른 인간 플레이어들과 직접 결투할 수 있습니다." - campaign_player_created: "사용자 직접 제작" - campaign_player_created_description: "... 당신 동료가 고안한 레벨에 도전하세요 <a href=\"/contributeartisan\">마법사 장인</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." + email_invalid: "이메일 주소가 올바르지 않습니다." +# form_blurb: "Enter your parent's email below and we’ll show them!" + form_label: "이메일" + placeholder: "이메일" +# title: "Excellent Work, Apprentice" login: sign_up: "계정 생성" log_in: "로그인" logging_in: "로그인 중" log_out: "로그아웃" - recover: "계정 복구" + forgot_password: "비밀번호를 잊으셨나요?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + finishing: "완료중.." + sign_in_with_facebook: "Facebook으로 로그인" + sign_in_with_gplus: "G+로 로그인" + signup_switch: "새로운 계정을 만드세요." signup: - create_account_title: "진행 상황을 저장하기 위해서 새 계정을 생성합니다" - description: "이것은 무료입니다. 계속 진행하기 위해서 간단한 몇가지만 적어주세요" email_announcements: "안내 사항을 메일로 받겠습니다" - coppa: "13살 이상 또는 미국 외 거주자" - coppa_why: "(왜?)" creating: "계정을 생성 중입니다..." sign_up: "등록" log_in: "비밀번호로 로그인" social_signup: "또는 페이스북이나 구글 플러스로 계정을 만들 수 있습니다." required: "진행하기 전에 로그인이 필요합니다." + login_switch: "이미 계정이 있으신가요?" recover: recover_account_title: "계정 복구" send_password: "복구 비밀번호 전송" -# recovery_sent: "Recovery email sent." + recovery_sent: "메일 전송 완료" -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "주 장비" + secondary: "보조 장비" + armor: "갑옷" + accessories: "액세서리" + misc: "잡동사니" + books: "책" common: + back: "뒤로가기" # When used as an action verb, like "Navigate backward" + continue: "계속" # When used as an action verb, like "Continue forward" loading: "로딩중입니다..." saving: "저장중입니다..." sending: "보내는 중입니다..." @@ -139,19 +140,37 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t fork: "Fork" play: "시작" # When used as an action verb, like "Play next level" retry: "재시도" + actions: "행동" + info: "정보" + help: "도움말" watch: "보기" unwatch: "보기 해제" submit_patch: "패치 제출" + submit_changes: "변경사항 제출" + save_changes: "변경사항 저장" general: and: "그리고" name: "이름" -# date: "Date" + date: "날짜" body: "구성" version: "버전" + pending: "적용중" + accepted: "적용됨" + rejected: "거부됨" + withdrawn: "취소됨" + submitter: "제출자" + submitted: "제출됨" commit_msg: "커밋 메세지" + review: "리뷰" version_history: "버전 히스토리" version_history_for: "버전 히스토리 : " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" + undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" + redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" result: "결과" results: "결과들" description: "설명" @@ -173,65 +192,68 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t medium: "중급" hard: "상급" player: "플레이어" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "플레이어 레벨" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "전사" + ranger: "레인저" + wizard: "마법사" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "초" + seconds: "초" + minute: "분" + minutes: "분" + hour: "시간" + hours: "시간" + day: "일" + days: "일" + week: "주" + weeks: "주" + month: "개월" + months: "개월" + year: "년" + years: "년" play_level: done: "완료" home: "홈" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "레벨" # Like "Level: Dungeons of Kithgard" + skip: "넘어가기" + game_menu: "게임 메뉴" guide: "가이드" restart: "재시작" - goals: "목표" -# goal: "Goal" -# running: "Running..." + goals: "목표들" + goal: "목표" + running: "실행중..." success: "성공!" incomplete: "목표 미완료" timed_out: "제한 시간 초과" failing: "다시 한번 더 도전해보세요." action_timeline: "액션 타임라인" click_to_select: "유닛을 선택하기 위해서 유닛을 마우스로 클릭하세요." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "멀티플레이어" + control_bar_join_game: "게임입장" + reload: "새로고침" reload_title: "모든 코드가 다시 로딩 되었나요?" reload_really: "모든 레벨 초기화합니다. 확실한가요?" reload_confirm: "모두 초기화" + victory: "승리" victory_title_prefix: "" victory_title_suffix: " 완료" victory_sign_up: "진행사항 저장을 위해 등록하세요" victory_sign_up_poke: "코드를 저장하고 싶으세요? 지금 등록하세요!" victory_rate_the_level: "이번 레벨 평가: " # Only in old-style levels. victory_return_to_ladder: "레더로 돌아가기" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "다음 레벨 플레이 하기" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "계속하기" + victory_saving_progress: "저장하기" victory_go_home: "홈으로" # Only in old-style levels. victory_review: "리뷰를 남겨주세요" # Only in old-style levels. victory_hour_of_code_done: "정말 종료합니까?" victory_hour_of_code_done_yes: "네 내 Hour of Code™ 완료했습니다!" + victory_experience_gained: "획득한 경험치" + victory_gems_gained: "획득한 젬" + victory_new_item: "새로운 아이템" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." + victory_become_a_viking: "바이킹이 되세요" guide_title: "가이드" tome_minion_spells: "미니언의 마법" # Only in old-style levels. tome_read_only_spells: "읽기 전용 마법" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "누군가를 선택하세요. " tome_available_spells: "사용 가능한 마법" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -253,17 +276,24 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t keyboard_shortcuts: "단축키" loading_ready: "준비!" # loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" + problem_alert_title: "코드를 수정하세요" + problem_alert_help: "도와주세요" + time_current: "현재:" + time_total: "최대:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "다시 시도해보세요." infinite_loop_reset_level: "레벨 리셋" infinite_loop_comment_out: "내 코드를 일시적 주석처리하기" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." + tip_toggle_play: "Ctrl+P로 실행을 계속하거나 멈출수 있어요" + tip_scrub_shortcut: "Ctrl+[, Ctrl+] 를 이용해 실행 속도를 빠르게 할 수 있어요" tip_guide_exists: "화면 상단의 가이드를 클릭해보세요. 유용한 정보를 얻을 수 있습니다." tip_open_source: "코드 컴뱃은 100% 오픈 소스 기반입니다!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "코드 컴뱃은 2013년 10월에 베타 서비스를 출시했습니다." tip_think_solution: "해결 방법을 고민해보세요, 문제를 고민하지 말구요" # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -279,7 +309,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." # tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." # tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" + tip_no_try: "하든가 하지 말든가. 시도같은 건 없어. - 요다" # tip_patience: "Patience you must have, young Padawan. - Yoda" # tip_documented_bug: "A documented bug is not a bug; it is a feature." tip_impossible: "성공하기 전까진 불가능해 보이는 법이죠. - Nelson Mandela" @@ -289,27 +319,62 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "사용자 정의 마법사" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." + tip_code_never_lies: "코드는 절대로 거짓말을 하지 않는다. 주석은 가끔 하지만. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." + tip_google: "문제가 너무 어렵다구요? 구글로 검색해보세요!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "인벤토리" + save_load_tab: "저장하기/불러오기" + options_tab: "옵션" + guide_tab: "가이드" + guide_video_tutorial: "영상 튜토리얼" + guide_tips: "팁들" multiplayer_tab: "멀티 플레이" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" + auth_tab: "가입하기" + inventory_caption: "장비 장착" + choose_hero_caption: "영웅 및 언어 선택 " # save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + options_caption: "설정들을 바꾸기" + guide_caption: "문서들과 팁들" + multiplayer_caption: "친구들과 플레이 하세요!" + auth_caption: "진행사항을 저장하세요" -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" + leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" + day: "오늘" + week: "이번 주" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + + inventory: + choose_inventory: "아이템 장착하기" + equipped_item: "장착됨" +# required_purchase_title: "Required" + available_item: "사용 가능" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" # equipped: "(equipped)" @@ -325,10 +390,84 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: # choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" + programming_language: "프로그래밍 언어" # programming_language_description: "Which programming language do you want to use?" # default: "Default" # experimental: "Experimental" @@ -339,7 +478,8 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t lua_blurb: "게임 스크립팅 언어" io_blurb: "간단하지만 아직 잘 알려지지 않은 언어." # status: "Status" -# weapons: "Weapons" +# hero_type: "Type" + weapons: "무기" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" # weapons_wizard: "Wands, Staffs - Long Range, Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -375,11 +527,9 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t options: # general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." + volume_label: "볼륨" + music_label: "음악" + music_description: "배경음악 ON/OFF" editor_config: "에디터 설정" editor_config_title: "에디터 설정" editor_config_level_language_label: "이 레벨에서 사용할 언어" @@ -411,35 +561,129 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" + team: "팀" + george_title: "최고경영자" # {change} # george_blurb: "Businesser" -# scott_title: "Programmer" + scott_title: "프로그래머" # {change} # scott_blurb: "Reasonable One" -# nick_title: "Programmer" + nick_title: "프로그래머" # {change} # nick_blurb: "Motivation Guru" -# michael_title: "Programmer" + michael_title: "프로그래머" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + matt_title: "프로그래머" # {change} + matt_blurb: "자전거 타는 사람" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "새로운 버전을 저장합니다" new_major_version: "신규 버전" +# submitting_patch: "Submitting Patch..." cla_prefix: "변경사항을 저장하기 위해서는, 먼저 계약사항에 동의 하셔야 합니다." cla_url: "CLA" cla_suffix: "." cla_agree: "동의 합니다" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "코드컴뱃에 전할 말" welcome: "언제든 의견을 보내주세요. 이 양식을 이메일에 사용해 주세요!" - contribute_prefix: "혹시 같이 코드컴뱃에 공헌하고 싶으시다면 홈페이지를 방문해주세요." - contribute_page: "참여하기 페이지" - contribute_suffix: "!" forum_prefix: "공개적으로 논의할 사항이라면 우리 포럼에서 해주세요 : " forum_page: "포럼" forum_suffix: " 대신에." +# faq_prefix: "There's also a" + faq: "자주 묻는 질문" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "의견 보내기" contact_candidate: "지원자에게 연락하기" # Deprecated recruitment_reminder: "인터뷰를 원하는 지원자에게 연락하고자 할 때, 이 양식을 사용해주세요. 코드 컴뱃에게 반드시 첫 해 연봉의 15%를 지급해야합니다. 수수료는 직원을 고용하자마자 즉시 지급되어야 합니다. 한편 90일 이내로 채용이 취소된다면 수수료를 환불받을 수 있습니다. 아르바이트, 재택근무, 계약직은 인턴의 경우와 마찬가지로 수수료가 없습니다." # Deprecated @@ -450,21 +694,28 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t autosave: "변경 사항은 자동 저장 됩니다" me_tab: "나" picture_tab: "사진" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" upload_picture: "사진 업로드" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "비밀번호" emails_tab: "이메일" admin: "관리자" new_password: "새 비밀번호" new_password_verify: "확인(다시한번 입력해주세요)" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "이메일 구독" -# email_subscriptions_none: "No Email Subscriptions." + email_subscriptions_none: "이메일 구독 안 함" email_announcements: "공지사항" email_announcements_description: "코드 컴뱃의 개발 및 진행 상황을 이메일로 구독하세요" email_notifications: "알람" email_notifications_summary: "당신의 코드 컴뱃 활동과 관련된 자동 알림 메일을 설정할 수 있습니다." email_any_notes: "모든 알림 받기" email_any_notes_description: "모든 알림 메일 받지 않기" -# email_news: "News" + email_news: "뉴스" email_recruit_notes: "구인 정보" email_recruit_notes_description: "정말 실력이 좋으시다고 판단되면, 보다 좋은 구직 정보와 관련하여 연락드릴 수도 있습니다." contributor_emails: "조력자들 이메일" @@ -481,15 +732,14 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" view_profile: "나의 프로필 보기" - wizard_tab: "마법사" - wizard_color: "마법사 옷 색깔" keyboard_shortcuts: keyboard_shortcuts: "단축키" space: "스페이스" enter: "엔터" +# press_enter: "press enter" escape: "Esc" -# shift: "Shift" + shift: "Shift" # run_code: "Run current code." # run_real_time: "Run in real time." # continue_script: "Continue past current script." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." community: main_title: "코드 컴뱃 커뮤니티" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "대마법사" archmage_title_description: "(코더)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "장인" artisan_title_description: "(레벨 제작자)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "모험가" adventurer_title_description: "(레벨 테스터)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "작가" scribe_title_description: "(기사 에디터)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "외교관" diplomat_title_description: "(번역가)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "대사" ambassador_title_description: "(지원)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: main_title: "코드 컴뱃 에디터들" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t thang_title: "Thang 에디터" level_title: "레벨 에디터" achievement_title: "업적 에디터" +# poll_title: "Poll Editor" back: "뒤로" revert: "되돌리기" revert_models: "모델 되돌리기" pick_a_terrain: "지형을 선택하세요." - small: "작게" + dungeon: "지하 감옥" +# indoor: "Indoor" + desert: "사막" grassy: "풀로 덮인" +# mountain: "Mountain" +# glacier: "Glacier" + small: "작게" + large: "크게" fork_title: "새 버전 가져오기" fork_creating: "포크 생성중..." # generate_terrain: "Generate Terrain" more: "더 보기" wiki: "위키" live_chat: "실시간 채팅" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" level_some_options: "다른 옵션들?" level_tab_thangs: "Thangs" level_tab_scripts: "스크립트들" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # level_tab_thangs_all: "All" level_tab_thangs_conditions: "컨디션 시작" level_tab_thangs_add: "Thangs 추가" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" delete: "삭제" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" level_settings_title: "설정" level_component_tab_title: "현재 요소들" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t new_level_title_login: "새로운 레벨을 만드시려면 로그인하세요." # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" article_search_title: "기사들은 여기에서 찾으세요" thang_search_title: "Thang 타입들은 여기에서 찾으세요" level_search_title: "레벨들은 여기에서 찾으세요" achievement_search_title: "업적 검색" +# poll_search_title: "Search Polls" read_only_warning2: "주의: 로그인하지 않으셨기 때문에 내용을 저장할 수 없습니다." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "미리보기" edit_article_title: "기사 편집하기" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" rules: "규칙" winners: "승리자" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "서버로부터 로딩하는 데 문제가 발생했습니다." @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t leaderboard: "상위권 순위 차트" # user_schema: "User Schema" user_profile: "유저 프로필" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -837,17 +1174,44 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # user_remark: "User Remark" # user_remarks: "User Remarks" # versions: "Versions" -# items: "Items" + items: "아이템" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" -# clas: "CLAs" + clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." privacy_title: "프라이버시" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." email_title: "이메일" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t email_settings_url: "이메일 설정" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t license: "라이센스" # oreilly: "ebook of your choice" - wizard_settings: - title: "마법사 설장" - customize_avatar: "당신의 아바타를 직접 꾸미세요" - active: "활성화" - color: "색상" - group: "종류" - clothes: "옷" - trim: "장식" - cloud: "구름" - team: "팀" - spell: "마법" - boots: "장화" - hue: "색조" - saturation: "채도" - lightness: "명도" - account_profile: settings: "설정" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "프로필 수정하기" diff --git a/app/locale/locale.coffee b/app/locale/locale.coffee index d9e3c3300..b07ef8d61 100644 --- a/app/locale/locale.coffee +++ b/app/locale/locale.coffee @@ -10,56 +10,59 @@ module.exports = continue if path is 'locale/locale' code = path.replace('locale/', '') @[code] = require(path) - - + + 'en': { nativeDescription: 'English', englishDescription: 'English' } 'en-US': { nativeDescription: 'English (US)', englishDescription: 'English (US)' } 'en-GB': { nativeDescription: 'English (UK)', englishDescription: 'English (UK)' } - 'en-AU': { nativeDescription: 'English (AU)', englishDescription: 'English (AU)' } + 'zh-HANS': { nativeDescription: '简体中文', englishDescription: 'Chinese (Simplified)' } + 'zh-HANT': { nativeDescription: '繁体中文', englishDescription: 'Chinese (Traditional)' } 'ru': { nativeDescription: 'русский', englishDescription: 'Russian' } + 'es-ES': { nativeDescription: 'español (ES)', englishDescription: 'Spanish (Spain)' } + 'es-419': { nativeDescription: 'español (América Latina)', englishDescription: 'Spanish (Latin America)' } + 'fr': { nativeDescription: 'français', englishDescription: 'French' } + # Begin alphabetized list: https://github.com/codecombat/codecombat/issues/2329#issuecomment-74630546 + 'ar': { nativeDescription: 'العربية', englishDescription: 'Arabic' } + 'bg': { nativeDescription: 'български език', englishDescription: 'Bulgarian' } + 'ca': { nativeDescription: 'Català', englishDescription: 'Catalan' } + 'cs': { nativeDescription: 'čeština', englishDescription: 'Czech' } + 'da': { nativeDescription: 'dansk', englishDescription: 'Danish' } 'de-DE': { nativeDescription: 'Deutsch (Deutschland)', englishDescription: 'German (Germany)' } 'de-AT': { nativeDescription: 'Deutsch (Österreich)', englishDescription: 'German (Austria)' } 'de-CH': { nativeDescription: 'Deutsch (Schweiz)', englishDescription: 'German (Switzerland)' } - 'es-419': { nativeDescription: 'español (América Latina)', englishDescription: 'Spanish (Latin America)' } - 'es-ES': { nativeDescription: 'español (ES)', englishDescription: 'Spanish (Spain)' } - 'zh-HANS': { nativeDescription: '简体中文', englishDescription: 'Chinese (Simplified)' } - 'zh-HANT': { nativeDescription: '繁体中文', englishDescription: 'Chinese (Traditional)' } - 'zh-WUU-HANS': { nativeDescription: '吴语', englishDescription: 'Wuu (Simplified)' } - 'zh-WUU-HANT': { nativeDescription: '吳語', englishDescription: 'Wuu (Traditional)' } - 'fr': { nativeDescription: 'français', englishDescription: 'French' } - 'ja': { nativeDescription: '日本語', englishDescription: 'Japanese' } - 'ar': { nativeDescription: 'العربية', englishDescription: 'Arabic' } - 'pt-BR': { nativeDescription: 'português do Brasil', englishDescription: 'Portuguese (Brazil)' } - 'pt-PT': { nativeDescription: 'Português (Portugal)', englishDescription: 'Portuguese (Portugal)' } - 'pl': { nativeDescription: 'język polski', englishDescription: 'Polish' } + 'el': { nativeDescription: 'Ελληνικά', englishDescription: 'Greek' } + 'eo': { nativeDescription: 'Esperanto', englishDescription: 'Esperanto' } + 'fa': { nativeDescription: 'فارسی', englishDescription: 'Persian' } + 'gl': { nativeDescription: 'Galego', englishDescription: 'Galician' } + 'ko': { nativeDescription: '한국어', englishDescription: 'Korean' } + 'id': { nativeDescription: 'Bahasa Indonesia', englishDescription: 'Indonesian' } 'it': { nativeDescription: 'Italiano', englishDescription: 'Italian' } - 'tr': { nativeDescription: 'Türkçe', englishDescription: 'Turkish' } + 'he': { nativeDescription: 'עברית', englishDescription: 'Hebrew' } + 'hu': { nativeDescription: 'magyar', englishDescription: 'Hungarian' } + 'lt': { nativeDescription: 'lietuvių kalba', englishDescription: 'Lithuanian' } + 'mk-MK': { nativeDescription: 'Македонски', englishDescription: 'Macedonian' } + 'hi': { nativeDescription: 'मानक हिन्दी', englishDescription: 'Hindi' } + 'ms': { nativeDescription: 'Bahasa Melayu', englishDescription: 'Bahasa Malaysia' } + 'my': { nativeDescription: 'မြန်မာစကား', englishDescription: 'Myanmar language' } 'nl-BE': { nativeDescription: 'Nederlands (België)', englishDescription: 'Dutch (Belgium)' } 'nl-NL': { nativeDescription: 'Nederlands (Nederland)', englishDescription: 'Dutch (Netherlands)' } - 'fa': { nativeDescription: 'فارسی', englishDescription: 'Persian' } - 'cs': { nativeDescription: 'čeština', englishDescription: 'Czech' } - 'sv': { nativeDescription: 'Svenska', englishDescription: 'Swedish' } - 'id': { nativeDescription: 'Bahasa Indonesia', englishDescription: 'Indonesian' } - 'el': { nativeDescription: 'Ελληνικά', englishDescription: 'Greek' } + 'ja': { nativeDescription: '日本語', englishDescription: 'Japanese' } + 'nb': { nativeDescription: 'Norsk Bokmål', englishDescription: 'Norwegian (Bokmål)' } + 'nn': { nativeDescription: 'Norsk Nynorsk', englishDescription: 'Norwegian (Nynorsk)' } + 'uz': { nativeDescription: "O'zbekcha", englishDescription: 'Uzbek' } + 'pl': { nativeDescription: 'język polski', englishDescription: 'Polish' } + 'pt-PT': { nativeDescription: 'Português (Portugal)', englishDescription: 'Portuguese (Portugal)' } + 'pt-BR': { nativeDescription: 'português do Brasil', englishDescription: 'Portuguese (Brazil)' } 'ro': { nativeDescription: 'limba română', englishDescription: 'Romanian' } - 'vi': { nativeDescription: 'Tiếng Việt', englishDescription: 'Vietnamese' } - 'hu': { nativeDescription: 'magyar', englishDescription: 'Hungarian' } - 'th': { nativeDescription: 'ไทย', englishDescription: 'Thai' } - 'da': { nativeDescription: 'dansk', englishDescription: 'Danish' } - 'ko': { nativeDescription: '한국어', englishDescription: 'Korean' } + 'sr': { nativeDescription: 'српски', englishDescription: 'Serbian' } 'sk': { nativeDescription: 'slovenčina', englishDescription: 'Slovak' } 'sl': { nativeDescription: 'slovenščina', englishDescription: 'Slovene' } 'fi': { nativeDescription: 'suomi', englishDescription: 'Finnish' } - 'bg': { nativeDescription: 'български език', englishDescription: 'Bulgarian' } - 'no': { nativeDescription: 'Norsk', englishDescription: 'Norwegian' } - 'nn': { nativeDescription: 'Norwegian Nynorsk', englishDescription: 'Norwegian' } - 'nb': { nativeDescription: 'Norsk Bokmål', englishDescription: 'Norwegian (Bokmål)' } - 'he': { nativeDescription: 'עברית', englishDescription: 'Hebrew' } - 'lt': { nativeDescription: 'lietuvių kalba', englishDescription: 'Lithuanian' } - 'sr': { nativeDescription: 'српски', englishDescription: 'Serbian' } + 'sv': { nativeDescription: 'Svenska', englishDescription: 'Swedish' } + 'th': { nativeDescription: 'ไทย', englishDescription: 'Thai' } + 'tr': { nativeDescription: 'Türkçe', englishDescription: 'Turkish' } 'uk': { nativeDescription: 'українська мова', englishDescription: 'Ukrainian' } - 'hi': { nativeDescription: 'मानक हिन्दी', englishDescription: 'Hindi' } 'ur': { nativeDescription: 'اُردُو', englishDescription: 'Urdu' } - 'ms': { nativeDescription: 'Bahasa Melayu', englishDescription: 'Bahasa Malaysia' } - 'ca': { nativeDescription: 'Català', englishDescription: 'Catalan' } - 'gl': { nativeDescription: 'Galego', englishDescription: 'Galician' } \ No newline at end of file + 'vi': { nativeDescription: 'Tiếng Việt', englishDescription: 'Vietnamese' } + 'zh-WUU-HANS': { nativeDescription: '吴语', englishDescription: 'Wuu (Simplified)' } + 'zh-WUU-HANT': { nativeDescription: '吳語', englishDescription: 'Wuu (Traditional)' } diff --git a/app/locale/lt.coffee b/app/locale/lt.coffee index f4074d540..76c874dac 100644 --- a/app/locale/lt.coffee +++ b/app/locale/lt.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # slogan: "Learn to Code by Playing a Game" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level +# play: "Play" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" # login: # sign_up: "Create Account" # log_in: "Log In" # logging_in: "Logging In" # log_out: "Log Out" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Loading..." # saving: "Saving..." # sending: "Sending..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # fork: "Fork" # play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/mk-MK.coffee b/app/locale/mk-MK.coffee new file mode 100644 index 000000000..9a7f1de04 --- /dev/null +++ b/app/locale/mk-MK.coffee @@ -0,0 +1,1478 @@ +module.exports = nativeDescription: "Македонски", englishDescription: "Macedonian", translation: + home: + slogan: "Научи да програмираш преку игра" + no_ie: "CodeCombat не работи во Internet Explorer верзија 8 или постара. Извини!" # Warning that only shows up in IE8 and older + no_mobile: "CodeCombat не е дизајнирана за мобилни уреди и може да не работи!" # Warning that shows up on mobile devices + play: "Играј" # The big play button that opens up the campaign view. + old_browser: "Уф, прелистувачот ти е премногу стар за да ја пушти CodeCombat. Извини!" # Warning that shows up on really old Firefox/Chrome/Safari + old_browser_suffix: "Можеш да пробаш и покрај тоа, но најверојатно нема да работи." + ipad_browser: "Лоши вести: CodeCombat не работи во прелистувачот на iPad. Добри вести: Нашата апликација за iPad е готова и чека одобрение од Apple." + campaign: "Кампања" + for_beginners: "За почетници" + multiplayer: "Повеќе играчи" # Not currently shown on home page + for_developers: "За Developer-и" # Not currently shown on home page. + or_ipad: "Или симни за iPad" + + nav: + play: "Нивоа" # The top nav bar entry where players choose which levels to play + community: "Заедница" + editor: "Едитор" + blog: "Блог" + forum: "Форум" + account: "Сметка" + profile: "Профил" + stats: "Статистики" +# code: "Code" + admin: "Админ" # Only shows up when you are an admin + home: "Дома" + contribute: "Допринеси" + legal: "Законски" + about: "За CodeCombat" + contact: "Контакт" + twitter_follow: "Следи" + teachers: "Учители" + + modal: + close: "Затвори" + okay: "Во ред" + + not_found: + page_not_found: "Страницата не е најдена" + + diplomat_suggestion: + title: "Помогни да се преведе CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. + sub_heading: "Ни требаат твоите јазични вештини." + pitch_body: "Ние ја развиваме CodeCombat на англиски, но веќе имаме играчи низ целиот свет. Многу од нив сакаат да играат на македонски, а не разбираат англиски, па ако ги зборуваш и двата јазика, размисли дали би сакал/а да се зачлениш како Дипломат и да помогнеш да се преведат на македонски CodeCombat веб сајтот и сите нивоа од играта." + missing_translations: "Додека не преведеме сè на македонски, содржината ќе биде на англиски каде што македонскиот не е достапен." + learn_more: "Научи повеќе за тоа како е да се биде Дипломат" + subscribe_as_diplomat: "Зачлени се како Дипломат" + + play: + play_as: "Играј како" # Ladder page + spectate: "Набљудувај" # Ladder page + players: "играчи" # Hover over a level on /play + hours_played: "изиграни часови" # Hover over a level on /play + items: "Опрема" # Tooltip on item shop button from /play + unlock: "Отклучи" # For purchasing items and heroes + confirm: "Потврди" + owned: "Имаш" # For items you own + locked: "Заклучено" + purchasable: "Може да се купи" # For a hero you unlocked but haven't purchased + available: "Достапно" + skills_granted: "Доделени вештини" # Property documentation details + heroes: "Херои" # Tooltip on hero shop button from /play + achievements: "Постигнувања" # Tooltip on achievement list button from /play + account: "Сметка" # Tooltip on account button from /play + settings: "Подесувања" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "Следно" # Go from choose hero to choose inventory before playing a level + change_hero: "Смени херој" # Go back from choose inventory to choose hero + choose_inventory: "Опреми се" + buy_gems: "Купи скапоцени камења" + subscription_required: "Потребно е зачленување" + anonymous: "Анонимен играч" + level_difficulty: "Тешкотија: " + campaign_beginner: "Почетничка кампања" + awaiting_levels_adventurer_prefix: "Пуштаме пет нивоа неделно." # {change} + awaiting_levels_adventurer: "Зачлени се како Авантурист" + awaiting_levels_adventurer_suffix: "за да бидеш првиот кој ќе ги игра новите нивоа." +# adjust_volume: "Adjust volume" + campaign_multiplayer: "Арени за повеќе играчи" + campaign_multiplayer_description: "... во кои кодираш лице-во-лице против други играчи." +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Одлично напредуваш! Кажи му на родителот колку имаш научено со CodeCombat." + email_invalid: "E-mail адресата е невалидна." + form_blurb: "Внеси ја e-mail адресата на твојот родител подолу и ние ќе му покажеме!" + form_label: "E-mail адреса" + placeholder: "e-mail адреса" + title: "Одлична работа, чираку" + + login: + sign_up: "Направи сметка" + log_in: "Најави се" + logging_in: "Најавувањето е во тек" + log_out: "Одјави се" + forgot_password: "Ја заборави твојата лозинка?" + authenticate_gplus: "Провери G+ најава" + load_profile: "Вчитај G+ профил" + finishing: "Завршување" + sign_in_with_facebook: "Најави се со Facebook" + sign_in_with_gplus: "Најави се со G+" + signup_switch: "Сакаш да направиш сметка?" + + signup: + email_announcements: "Примај соопштенија преку e-mail" + creating: "Сметката се прави..." + sign_up: "Направи сметка" + log_in: "најави се со лозинка" + social_signup: "Или, можеш да се пријавиш преку Facebook или G+:" + required: "Мораш да се најавиш за да имаш пристап таму." + login_switch: "Веќе имаш сметка?" + + recover: + recover_account_title: "Врати сметка" + send_password: "Испрати лозинка за враќање" + recovery_sent: "E-mail-от за враќање на лозинката е испратен." + + items: + primary: "Главно" + secondary: "Споредно" + armor: "Оклоп" + accessories: "Додатоци" + misc: "Разно" + books: "Книги" + + common: + back: "Врати се" # When used as an action verb, like "Navigate backward" + continue: "Продолжи" # When used as an action verb, like "Continue forward" + loading: "Вчитување..." + saving: "Зачувување..." + sending: "Испраќање..." + send: "Испрати" + cancel: "Откажи" + save: "Зачувај" + publish: "Објави" + create: "Направи" + manual: "Рачно" +# fork: "Fork" + play: "Играј" # When used as an action verb, like "Play next level" + retry: "Обиди се повторно" + actions: "Дејствија" + info: "Инфо" + help: "Помош" +# watch: "Watch" +# unwatch: "Unwatch" + submit_patch: "Поднеси закрпа" + submit_changes: "Поднеси промени" + save_changes: "Зачувај промени" + + general: + and: "и" + name: "Име" + date: "Датум" + body: "Тело" + version: "Верзија" +# pending: "Pending" + accepted: "Прифатено" + rejected: "Одбиено" + withdrawn: "Повлечено" + submitter: "Подносител" + submitted: "Поднесено" + commit_msg: "Порака за поднесокот" + review: "Проверка" + version_history: "Историја на верзии" + version_history_for: "Историја на верзии за: " + select_changes: "Одбери две промени подолу за да ја видиш разликата." +# undo_prefix: "Undo" + undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Пушти преглед на моменталното ниво" + result: "Резултат" + results: "Резултати" + description: "Опис" + or: "или" + subject: "Предмет на пораката" + email: "E-mail" + password: "Лозинка" + message: "Порака" +# code: "Code" + ladder: "Ранг листа" + when: "Кога" + opponent: "Противник" + rank: "Ранг" +# score: "Score" + win: "Победа" + loss: "Изгубено" + tie: "Нерешено" + easy: "Лесно" + medium: "Средно" + hard: "Тешко" + player: "Играч" + player_level: "Ниво" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Воин" + ranger: "Ренџер" + wizard: "Волшебник" + + units: + second: "секунда" + seconds: "секунди" + minute: "минута" + minutes: "минути" + hour: "час" + hours: "часови" + day: "ден" + days: "денови" + week: "недела" + weeks: "недели" + month: "месец" + months: "месеци" + year: "година" + years: "години" + + play_level: + done: "Готово" + home: "Дома" # Not used any more, will be removed soon. + level: "Ниво" # Like "Level: Dungeons of Kithgard" + skip: "Прескокни" + game_menu: "Мени" + guide: "Водич" + restart: "Почни одново" + goals: "Цели" + goal: "Цел" + running: "Се извршува..." + success: "Успешно!" + incomplete: "Незавршено" + timed_out: "Истече времето" +# failing: "Failing" +# action_timeline: "Action Timeline" +# click_to_select: "Click on a unit to select it." + control_bar_multiplayer: "Повеќе играчи" + control_bar_join_game: "Приклучи се во игра" +# reload: "Reload" +# reload_title: "Reload All Code?" +# reload_really: "Are you sure you want to reload this level back to the beginning?" +# reload_confirm: "Reload All" + victory: "Победа" +# victory_title_prefix: "" +# victory_title_suffix: " Complete" + victory_sign_up: "Направи сметка за да го зачуваш напредокот" + victory_sign_up_poke: "Сакаш да го зачуваш твојот код? Направи бесплатна сметка!" + victory_rate_the_level: "Оцени го нивото: " # Only in old-style levels. + victory_return_to_ladder: "Врати се кај ранг листата" + victory_play_continue: "Продолжи" + victory_saving_progress: "Напредокот се зачувува" + victory_go_home: "Оди дома" # Only in old-style levels. + victory_review: "Кажи ни повеќе!" # Only in old-style levels. + victory_hour_of_code_done: "Дали си готов?" +# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" + victory_experience_gained: "Добиено искуство" + victory_gems_gained: "Добиени скапоцени камења" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" + guide_title: "Водич" +# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. +# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. +# tome_other_units: "Other Units" # Only in old-style levels. +# tome_cast_button_run: "Run" +# tome_cast_button_running: "Running" +# tome_cast_button_ran: "Ran" +# tome_submit_button: "Submit" +# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. +# tome_select_method: "Select a Method" +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). +# tome_select_a_thang: "Select Someone for " +# tome_available_spells: "Available Spells" + tome_your_skills: "Твои вештини" + tome_help: "Помош" +# tome_current_method: "Current Method" + hud_continue_short: "Продолжи" + code_saved: "Кодот е зачуван" + skip_tutorial: "Прескокни (esc)" + keyboard_shortcuts: "Кратенки на тастатурата" + loading_ready: "Готово!" + loading_start: "Почни ниво" + problem_alert_title: "Поправи си го кодот" + problem_alert_help: "Помош" + time_current: "Сега:" + time_total: "Максимум:" + time_goto: "Оди до:" + non_user_code_problem_title: "Неможам да го вчитам нивото" + infinite_loop_title: "Забележана е бесконечна јамка" + infinite_loop_description: "Првичниот код за изградба на светот никогаш не завршил со своето извршување. Веројатно е, или премногу спор, или соджи бесконечна јамка. Или можеби има грешка. Можеш да се обидеш да го извршиш овој код повторно или да го ресетираш кодот во првичната состојба. Ако тоа не го поправи проблемот, те молиме да не известиш." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "Обиди се повторно" + infinite_loop_reset_level: "Ресетирај ниво" + infinite_loop_comment_out: "Искоментирај го мојот код" + tip_toggle_play: "Пушти/Паузирај со Ctrl+P." + tip_scrub_shortcut: "Ctrl+[ и Ctrl+] премотуваат назад и напред." # {change} + tip_guide_exists: "Кликни на водичот, во менито (на врвот на страната), за корисни информации." + tip_open_source: "CodeCombat е 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat ја пушти својата бета верзија во октомври, 2013." + tip_think_solution: "Мисли на решението, не на проблемот." + tip_theory_practice: "Теоретски, не постои разлика помеѓу теорија и пракса. Но, во пракса, постои. - Yogi Berra" + tip_error_free: "Постојат два начина за пишување програми без грешки; само третиот работи. - Alan Perlis" + tip_debugging_program: "Ако дебагирање е процесот на отстранување грешки, тогаш програмирање мора да е процесот при кој тие настануваат. - Edsger W. Dijkstra" + tip_forums: "Упати се кон форумите и кажи ни што мислиш!" + tip_baby_coders: "Во иднината, дури и бебињата ќе бидат Веле-Волшебници." + tip_morale_improves: "Вчитувањето ќе продолжи се додека моралот не се подобри." + tip_all_species: "Веруваме во еднакви можности за изучување на програмирањето за сите видови." +# tip_reticulating: "Reticulating spines." + tip_harry: "Ти си волшебник, " + tip_great_responsibility: "Со големи програмерски вештини доаѓа и голема одговорност за дебагирање." +# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." + tip_binary: "Има само 10 вида на луѓе во светот: оние кои што разбираат бинарно, и оние кои не разбираат." + tip_commitment_yoda: "Програмер мора да ја има најдлабоката обврзаност, најсериозниот ум. ~ Yoda" + tip_no_try: "Направи. Или не прави. Нема обидување. - Yoda" + tip_patience: "Трпение мора да имаш, млад Padawan. - Yoda" + tip_documented_bug: "Документирана грешка не е грешка; тоа е едноставно нешто што програмот го прави." + tip_impossible: "Секогаш изледа невозможно, се додека некој не го направи. - Nelson Mandela" + tip_talk_is_cheap: "Зборувањето е евтино. Покажи ми го кодот. - Linus Torvalds" + tip_first_language: "Нај катастрофалната работа што можеш да ја научиш е твојот прв програмски јазик. - Alan Kay" + tip_hardware_problem: "Прашање: Колку програмери се потребни за да се смени сијалица? Одговор: Ниту еден, тоа е хардверски проблем." + tip_hofstadters_law: "Законот на Hofstadter: Секогаш треба повеќе време отколку што очекуваш, дури и кога ќе го земеш во предвид законот на Hofstadter." + tip_premature_optimization: "Предвремената оптимизација е коренот на сето зло. - Donald Knuth" + tip_brute_force: "Кога не си сигурен, користи 'brute force'. - Ken Thompson" + tip_extrapolation: "Има само два вида на луѓе: оние кои можат да екстраполираат од некомплетни податоци..." + tip_superpower: "Програмирањето е способност, најблиска до супермоќ, која ја имаме." + tip_control_destiny: "Во вистински open source, ти имаш право да ја контролираш сопствената судбина. - Linus Torvalds" + tip_no_code: "Ниеден код не е побрз од никаков код." + tip_code_never_lies: "Кодот никогаш не лаже, за разлика од коментарите. — Ron Jeffries" + tip_reusable_software: "За еден софтвер да биде употреблив на повеќе места прво треба да биде употреблив." + tip_optimization_operator: "Секој програмски јазик има оператор за оптимизација. Во повеќето јазици тој оператор е ‘//’" + tip_lines_of_code: "Да се мери напредок во програмирање со број на напишани линии код е исто као да се мери напредок при изградба на авион по неговата тежина. — Bill Gates" + tip_source_code: "Сакам да го сменам светот ама не ми го даваат кодот." + tip_javascript_java: "Java е поврзана со JavaScript колку што и тапа е поврзана со тапанар. - Chris Heilmann" + tip_move_forward: "Што и да правиш, продолжи да одиш напред. - Martin Luther King Jr." + tip_google: "Имаш проблем што не можеш да го решиш? Побарај на Google!" + tip_adding_evil: "Додавам трошка зло." + tip_hate_computers: "Тоа е работата со луѓето кои мислат дека мразат компјутери. Тоа што тие навистина го мразат се лоши програмери. - Larry Niven" + tip_open_source_contribute: "Можеш да помогнеш да се подобри CodeCombat!" + tip_recurse: "Да работиш итеративно е човечки, а да работич рекурзивно е божествено. - L. Peter Deutsch" + tip_free_your_mind: "Мораш да го оставиш сето тоа, Нео. Стравот, сомнежот, и невербата. Ослободи го твојот ум. - Morpheus" + tip_strong_opponents: "Дури и најсилниот противник има некоја слаба точка. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." + + game_menu: +# inventory_tab: "Inventory" + save_load_tab: "Зачувај/Вчитај" +# options_tab: "Options" + guide_tab: "Водич" + guide_video_tutorial: "Видео водич" + guide_tips: "Совети" + multiplayer_tab: "Повеќе играчи" + auth_tab: "Направи сметка" + inventory_caption: "Опреми го твојот херој" + choose_hero_caption: "Избери херој, јазик" + save_load_caption: "... и види историја" + options_caption: "Промени подесувања" + guide_caption: "Документи и совети" + multiplayer_caption: "Играј со пријатели!" + auth_caption: "Зачувај го твојот напредок." + +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + + inventory: +# choose_inventory: "Equip Items" +# equipped_item: "Equipped" + required_purchase_title: "Задолжително" + available_item: "Достапно" + restricted_title: "Забрането" +# should_equip: "(double-click to equip)" +# equipped: "(equipped)" + locked: "(заклучено)" + restricted: "(забрането во ова ниво)" +# equip: "Equip" +# unequip: "Unequip" + + buy_gems: + few_gems: "Неколку скапоцени камења" + pile_gems: "Купче скапоцени камења" + chest_gems: "Сандак скапоцени камења" + purchasing: "Купувам..." + declined: "Твојата картичка беше одбиена" + retrying: "Серверска грешка, се обидувам повторно." + prompt_title: "Немаш доволно скапоцени камења" + prompt_body: "Дали сакаш да земеш повеќе?" + prompt_button: "Влези во продавницата" + recovered: "Претходното купување на скапоцени камења е вратено од загуба. Те молам 'освежи' ја страната." +# price: "x3500 / mo" + + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" + subscribe_title: "Зачлени се" + unsubscribe: "Откажи членство" + confirm_unsubscribe: "Потврди откажување на членство" + never_mind: "Не е битно, сеуште те сакам" + thank_you_months_prefix: "Фала што нѐ поддржуваше овие последни" + thank_you_months_suffix: "месеци." + thank_you: "Фала што ја поддржуваш CodeCombat." + sorry_to_see_you_go: "Жал ни е што си одиш! Те молиме кажи ни што можевме да направиме подобро." + unsubscribe_feedback_placeholder: "Што направивме?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" + parents: "За родители" + parents_title: "Вашето дете ќе научи да програмира." # {change} + parents_blurb1: "Со CodeCombat, вашите деца учат преку пишување на вистински програмски код. Почнуваат со учење на едноставни команди, по што се продолжува на понапредни теми." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "За $9.99 американски долари месечно, добиваат нови предизвици секоја недела и лична поддршка преку e-mail, од страна на професионални програмери." # {change} + parents_blurb3: "Без ризик: 100% гаранција за враќање на парите, лесно откажување на членството со еден клик." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "Месечна членарина" + subscription_required_to_play: "Треба да бидеш зачленет за да го играш ова ниво." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" + + choose_hero: + choose_hero: "Избери го твојот херој" + programming_language: "Програмски јазик" + programming_language_description: "Кој програмски јазик сакаш да го користиш?" +# default: "Default" + experimental: "Експериментално" + python_blurb: "Едноставен, но моќен, одличен за почетници и експерти." + javascript_blurb: "Јазикот на веб-от. (Не е исто што и Java.)" + coffeescript_blurb: "Поубава JavaScript синтакса." + clojure_blurb: "Модерен Lisp." + lua_blurb: "Јазик за скриптирање на игри." + io_blurb: "Едноставен, но не така очигледен." + status: "Статус" +# hero_type: "Type" + weapons: "Оружја" + weapons_warrior: "Мечеви - Краток досег, нема магија" + weapons_ranger: "Самострели, Пушки - Долг досег, нема магија" + weapons_wizard: "Волшебни стапчиња, стапови - долг досег, има магија" + attack: "Напад" # Can also translate as "Attack" + health: "Здравје" + speed: "Брзина" + regeneration: "Заздравување" + range: "Досег" # As in "attack or visual range" + blocks: "Блокира" # As in "this shield blocks this much damage" + backstab: "Од зад грб" # As in "this dagger does this much backstab damage" + skills: "Вештини" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." + available_for_purchase: "Достапно за купување" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Ниво за да се отклучи:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Само одредени херои можат да го играат ова ниво." + + skill_docs: +# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this +# read_only: "read-only" + action_name: "име" +# action_cooldown: "Takes" +# action_specific_cooldown: "Cooldown" +# action_damage: "Damage" + action_range: "Досег" + action_radius: "Радиус" + action_duration: "Времетраење" + example: "Пример" + ex: "пр" # Abbreviation of "example" + current_value: "Моментална вредност" + default_value: "Стандардна вредност" + parameters: "Параметри" + returns: "Враќа" + granted_by: "Овозможено од" + + save_load: + granularity_saved_games: "Зачувани" + granularity_change_history: "Историја" + + options: + general_options: "Општи подесувања" # Check out the Options tab in the Game Menu while playing a level + volume_label: "Јачина на звук" + music_label: "Музика" + music_description: "Вклучи/Исклучи позадинска музика." + editor_config: "Подесувања на едитор" + editor_config_title: "Подесувања на едитор" + editor_config_level_language_label: "Јазик за ова ниво" + editor_config_level_language_description: "Избери го програмскиот јазик за ова конкретно ниво." + editor_config_default_language_label: "Стандарден програмски јазик" + editor_config_default_language_description: "Избери го програмскиот јазик во кој сакаш да програмираш кога започуваш нови нивоа." +# editor_config_keybindings_label: "Key Bindings" +# editor_config_keybindings_default: "Default (Ace)" + editor_config_keybindings_description: "Додава дополнителни кратенки познати од вообичаените едитори." + editor_config_livecompletion_label: "Автоматско дополнување 'во живо'" + editor_config_livecompletion_description: "Прикажува предлози за автоматско дополнување/довршување на командите додека куцаш." + editor_config_invisibles_label: "Прикажи невидливи знаци" + editor_config_invisibles_description: "Ги прикажува невидливите знаци, како на пример знакот за празно место." + editor_config_indentguides_label: "Прикажи водичи за 'вдлабнување'" + editor_config_indentguides_description: "Прикажува вертикални линии за подобар преглед на 'вдлабнувањaтa' на текстот." + editor_config_behaviors_label: "Паметно однесување" + editor_config_behaviors_description: "Автоматски ги затвара отворените загради и наводници." + + about: + why_codecombat: "Зошто CodeCombat?" + why_paragraph_1: "Ако сакаш да научиш да програмираш тоа не значи дека ти требаат предавања. Треба да напишеш многу код и да си поминеш одлично правејќи го тоа." + why_paragraph_2_prefix: "Тоа е суштината на програмирањето. Мора да биде забавно. Не забавно од типот" + why_paragraph_2_italic: "јеее значка" + why_paragraph_2_center: "туку забавно од типот" + why_paragraph_2_italic_caps: "НЕ МАМО, МОРАМ ДА ГО ЗАВРШАМ НИВОТО!" + why_paragraph_2_suffix: "Поради тоа CodeCombat е игра за повеќе играчи, а не низа игриви предавања. Нема да прекинеме се додека ти не можеш да прекинеш--ама овој пат, тоа е добра работа." + why_paragraph_3: "Ако веќе стануваш зависен од некоја игра, стани зависен од оваа и стани еден од волшебниците на технолошкото доба." + press_title: "Блогери/Печат" + press_paragraph_1_prefix: "Сакаш да пишуваш за нас? Слободно можеш да ги симнеш и користиш сите ресурси вклучени во нашиот" + press_paragraph_1_link: "пакет за печат" + press_paragraph_1_suffix: ". Сите логоа и слики можат да бидат користени без да не контактираш директно." + team: "Тим" + george_title: "(CEO) Извршен директор" # {change} + george_blurb: "Бизнисер" + scott_title: "Програмер" # {change} + scott_blurb: "Оној разумниот" + nick_title: "Програмер" # {change} + nick_blurb: "Мотивациски гуру" + michael_title: "Програмер" + michael_blurb: "Систем администратор" + matt_title: "Програмер" # {change} + matt_blurb: "Бициклист" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." + + versions: + save_version_title: "Зачувај нова верзија" + new_major_version: "Нова поголема(major) верзија" + submitting_patch: "Испраќам закрпа..." + cla_prefix: "За да ги зачуваш промените, мораш да го прифатиш нашиот" + cla_url: "CLA" + cla_suffix: "(договор за тие што допринесуваат)." + cla_agree: "ПРИФАЌАМ" +# owner_approve: "An owner will need to approve it before your changes will become visible." + +# contact: +# contact_us: "Contact CodeCombat" +# welcome: "Good to hear from you! Use this form to send us email. " +# forum_prefix: "For anything public, please try " +# forum_page: "our forum" +# forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" +# send: "Send Feedback" +# contact_candidate: "Contact Candidate" # Deprecated +# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated + +# account_settings: +# title: "Account Settings" +# not_logged_in: "Log in or create an account to change your settings." +# autosave: "Changes Save Automatically" +# me_tab: "Me" +# picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" +# upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" +# password_tab: "Password" +# emails_tab: "Emails" +# admin: "Admin" +# new_password: "New Password" +# new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." +# email_subscriptions: "Email Subscriptions" +# email_subscriptions_none: "No Email Subscriptions." +# email_announcements: "Announcements" +# email_announcements_description: "Get emails on the latest news and developments at CodeCombat." +# email_notifications: "Notifications" +# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." +# email_any_notes: "Any Notifications" +# email_any_notes_description: "Disable to stop all activity notification emails." +# email_news: "News" +# email_recruit_notes: "Job Opportunities" +# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." +# contributor_emails: "Contributor Class Emails" +# contribute_prefix: "We're looking for people to join our party! Check out the " +# contribute_page: "contribute page" +# contribute_suffix: " to find out more." +# email_toggle: "Toggle All" +# error_saving: "Error Saving" +# saved: "Changes Saved" +# password_mismatch: "Password does not match." +# password_repeat: "Please repeat your password." +# job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated +# job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." +# job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." +# sample_profile: "See a sample profile" +# view_profile: "View Your Profile" + +# keyboard_shortcuts: +# keyboard_shortcuts: "Keyboard Shortcuts" +# space: "Space" +# enter: "Enter" +# press_enter: "press enter" +# escape: "Escape" +# shift: "Shift" +# run_code: "Run current code." +# run_real_time: "Run in real time." +# continue_script: "Continue past current script." +# skip_scripts: "Skip past all skippable scripts." +# toggle_playback: "Toggle play/pause." +# scrub_playback: "Scrub back and forward through time." +# single_scrub_playback: "Scrub back and forward through time by a single frame." +# scrub_execution: "Scrub through current spell execution." +# toggle_debug: "Toggle debug display." +# toggle_grid: "Toggle grid overlay." +# toggle_pathfinding: "Toggle pathfinding overlay." +# beautify: "Beautify your code by standardizing its formatting." +# maximize_editor: "Maximize/minimize code editor." + +# community: +# main_title: "CodeCombat Community" +# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" +# level_editor_prefix: "Use the CodeCombat" +# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" +# thang_editor_prefix: "We call units within the game 'thangs'. Use the" +# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." +# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" +# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." +# find_us: "Find us on these sites" +# social_blog: "Read the CodeCombat blog on Sett" +# social_discource: "Join the discussion on our Discourse forum" +# social_facebook: "Like CodeCombat on Facebook" +# social_twitter: "Follow CodeCombat on Twitter" +# social_gplus: "Join CodeCombat on Google+" +# social_hipchat: "Chat with us in the public CodeCombat HipChat room" +# contribute_to_the_project: "Contribute to the project" + +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + +# classes: +# archmage_title: "Archmage" +# archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" +# artisan_title: "Artisan" +# artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." +# adventurer_title: "Adventurer" +# adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." +# scribe_title: "Scribe" +# scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." +# diplomat_title: "Diplomat" +# diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." +# ambassador_title: "Ambassador" +# ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." + +# editor: +# main_title: "CodeCombat Editors" +# article_title: "Article Editor" +# thang_title: "Thang Editor" +# level_title: "Level Editor" +# achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" +# back: "Back" +# revert: "Revert" +# revert_models: "Revert Models" +# pick_a_terrain: "Pick A Terrain" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" +# grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" +# fork_title: "Fork New Version" +# fork_creating: "Creating Fork..." +# generate_terrain: "Generate Terrain" +# more: "More" +# wiki: "Wiki" +# live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" +# level_some_options: "Some Options?" +# level_tab_thangs: "Thangs" +# level_tab_scripts: "Scripts" +# level_tab_settings: "Settings" +# level_tab_components: "Components" +# level_tab_systems: "Systems" +# level_tab_docs: "Documentation" +# level_tab_thangs_title: "Current Thangs" +# level_tab_thangs_all: "All" +# level_tab_thangs_conditions: "Starting Conditions" +# level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" +# delete: "Delete" +# duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" +# rotate: "Rotate" +# level_settings_title: "Settings" +# level_component_tab_title: "Current Components" +# level_component_btn_new: "Create New Component" +# level_systems_tab_title: "Current Systems" +# level_systems_btn_new: "Create New System" +# level_systems_btn_add: "Add System" +# level_components_title: "Back to All Thangs" +# level_components_type: "Type" +# level_component_edit_title: "Edit Component" +# level_component_config_schema: "Config Schema" +# level_component_settings: "Settings" +# level_system_edit_title: "Edit System" +# create_system_title: "Create New System" +# new_component_title: "Create New Component" +# new_component_field_system: "System" +# new_article_title: "Create a New Article" +# new_thang_title: "Create a New Thang Type" +# new_level_title: "Create a New Level" +# new_article_title_login: "Log In to Create a New Article" +# new_thang_title_login: "Log In to Create a New Thang Type" +# new_level_title_login: "Log In to Create a New Level" +# new_achievement_title: "Create a New Achievement" +# new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" +# article_search_title: "Search Articles Here" +# thang_search_title: "Search Thang Types Here" +# level_search_title: "Search Levels Here" +# achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" +# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." +# no_achievements: "No achievements have been added for this level yet." +# achievement_query_misc: "Key achievement off of miscellanea" +# achievement_query_goals: "Key achievement off of level goals" +# level_completion: "Level Completion" +# pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" + +# article: +# edit_btn_preview: "Preview" +# edit_article_title: "Edit Article" + +# polls: +# priority: "Priority" + +# contribute: +# page_title: "Contributing" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" +# alert_account_message_intro: "Hey there!" +# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." +# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." +# class_attributes: "Class Attributes" +# archmage_attribute_1_pref: "Knowledge in " +# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." +# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." +# how_to_join: "How To Join" +# join_desc_1: "Anyone can help out! Just check out our " +# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " +# join_desc_3: ", or find us in our " +# join_desc_4: "and we'll go from there!" +# join_url_email: "Email us" +# join_url_hipchat: "public HipChat room" +# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." +# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" +# artisan_introduction_suf: ", then this class might be for you." +# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" +# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." +# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" +# artisan_join_desc: "Use the Level Editor in these steps, give or take:" +# artisan_join_step1: "Read the documentation." +# artisan_join_step2: "Create a new level and explore existing levels." +# artisan_join_step3: "Find us in our public HipChat room for help." +# artisan_join_step4: "Post your levels on the forum for feedback." +# artisan_subscribe_desc: "Get emails on level editor updates and announcements." +# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." +# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." +# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." +# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" +# adventurer_forum_url: "our forum" +# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" +# adventurer_subscribe_desc: "Get emails when there are new levels to test." +# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " +# scribe_introduction_url_mozilla: "Mozilla Developer Network" +# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." +# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." +# contact_us_url: "Contact us" +# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" +# scribe_subscribe_desc: "Get emails about article writing announcements." +# diplomat_introduction_pref: "So, if there's one thing we learned from the " +# diplomat_launch_url: "launch in October" +# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." +# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" +# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" +# diplomat_i18n_page: "translations page" +# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." +# diplomat_join_pref_github: "Find your language locale file " +# diplomat_github_url: "on GitHub" +# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" +# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" +# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" +# ambassador_join_note_strong: "Note" +# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" +# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." +# changes_auto_save: "Changes are saved automatically when you toggle checkboxes." +# diligent_scribes: "Our Diligent Scribes:" +# powerful_archmages: "Our Powerful Archmages:" +# creative_artisans: "Our Creative Artisans:" +# brave_adventurers: "Our Brave Adventurers:" +# translating_diplomats: "Our Translating Diplomats:" +# helpful_ambassadors: "Our Helpful Ambassadors:" + +# ladder: +# please_login: "Please log in first before playing a ladder game." +# my_matches: "My Matches" +# simulate: "Simulate" +# simulation_explanation: "By simulating games you can get your game ranked faster!" +# simulate_games: "Simulate Games!" +# simulate_all: "RESET AND SIMULATE GAMES" +# games_simulated_by: "Games simulated by you:" +# games_simulated_for: "Games simulated for you:" +# games_simulated: "Games simulated" +# games_played: "Games played" +# ratio: "Ratio" +# leaderboard: "Leaderboard" +# battle_as: "Battle as " +# summary_your: "Your " +# summary_matches: "Matches - " +# summary_wins: " Wins, " +# summary_losses: " Losses" +# rank_no_code: "No New Code to Rank" +# rank_my_game: "Rank My Game!" +# rank_submitting: "Submitting..." +# rank_submitted: "Submitted for Ranking" +# rank_failed: "Failed to Rank" +# rank_being_ranked: "Game Being Ranked" +# rank_last_submitted: "submitted " +# help_simulate: "Help simulate games?" +# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." +# no_ranked_matches_pre: "No ranked matches for the " +# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." +# choose_opponent: "Choose an Opponent" +# select_your_language: "Select your language!" +# tutorial_play: "Play Tutorial" +# tutorial_recommended: "Recommended if you've never played before" +# tutorial_skip: "Skip Tutorial" +# tutorial_not_sure: "Not sure what's going on?" +# tutorial_play_first: "Play the Tutorial first." +# simple_ai: "Simple AI" +# warmup: "Warmup" +# friends_playing: "Friends Playing" +# log_in_for_friends: "Log in to play with your friends!" +# social_connect_blurb: "Connect and play against your friends!" +# invite_friends_to_battle: "Invite your friends to join you in battle!" +# fight: "Fight!" +# watch_victory: "Watch your victory" +# defeat_the: "Defeat the" +# tournament_started: ", started" +# tournament_ends: "Tournament ends" +# tournament_ended: "Tournament ended" +# tournament_rules: "Tournament Rules" +# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" +# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" +# tournament_blurb_blog: "on our blog" +# rules: "Rules" +# winners: "Winners" + +# user: +# stats: "Stats" +# singleplayer_title: "Singleplayer Levels" +# multiplayer_title: "Multiplayer Levels" +# achievements_title: "Achievements" +# last_played: "Last Played" +# status: "Status" +# status_completed: "Completed" +# status_unfinished: "Unfinished" +# no_singleplayer: "No Singleplayer games played yet." +# no_multiplayer: "No Multiplayer games played yet." +# no_achievements: "No Achievements earned yet." +# favorite_prefix: "Favorite language is " +# favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." + +# achievements: +# last_earned: "Last Earned" +# amount_achieved: "Amount" +# achievement: "Achievement" +# category_contributor: "Contributor" +# category_ladder: "Ladder" +# category_level: "Level" +# category_miscellaneous: "Miscellaneous" +# category_levels: "Levels" +# category_undefined: "Uncategorized" +# current_xp_prefix: "" +# current_xp_postfix: " in total" +# new_xp_prefix: "" +# new_xp_postfix: " earned" +# left_xp_prefix: "" +# left_xp_infix: " until level " +# left_xp_postfix: "" + +# account: +# recently_played: "Recently Played" +# no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" + +# loading_error: +# could_not_load: "Error loading from server" +# connection_failure: "Connection failed." +# unauthorized: "You need to be signed in. Do you have cookies disabled?" +# forbidden: "You do not have the permissions." +# not_found: "Not found." +# not_allowed: "Method not allowed." +# timeout: "Server timeout." +# conflict: "Resource conflict." +# bad_input: "Bad input." +# server_error: "Server error." +# unknown: "Unknown error." + +# resources: +# sessions: "Sessions" +# your_sessions: "Your Sessions" +# level: "Level" +# social_network_apis: "Social Network APIs" +# facebook_status: "Facebook Status" +# facebook_friends: "Facebook Friends" +# facebook_friend_sessions: "Facebook Friend Sessions" +# gplus_friends: "G+ Friends" +# gplus_friend_sessions: "G+ Friend Sessions" +# leaderboard: "Leaderboard" +# user_schema: "User Schema" +# user_profile: "User Profile" +# patch: "Patch" +# patches: "Patches" +# patched_model: "Source Document" +# model: "Model" +# system: "System" +# systems: "Systems" +# component: "Component" +# components: "Components" +# thang: "Thang" +# thangs: "Thangs" +# level_session: "Your Session" +# opponent_session: "Opponent Session" +# article: "Article" +# user_names: "User Names" +# thang_names: "Thang Names" +# files: "Files" +# top_simulators: "Top Simulators" +# source_document: "Source Document" +# document: "Document" +# sprite_sheet: "Sprite Sheet" +# employers: "Employers" +# candidates: "Candidates" +# candidate_sessions: "Candidate Sessions" +# user_remark: "User Remark" +# user_remarks: "User Remarks" +# versions: "Versions" +# items: "Items" +# hero: "Hero" +# heroes: "Heroes" +# achievement: "Achievement" +# clas: "CLAs" +# play_counts: "Play Counts" +# feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" + +# delta: +# added: "Added" +# modified: "Modified" +# not_modified: "Not Modified" +# deleted: "Deleted" +# moved_index: "Moved Index" +# text_diff: "Text Diff" +# merge_conflict_with: "MERGE CONFLICT WITH" +# no_changes: "No Changes" + +# guide: +# temp: "Temp" + +# multiplayer: +# multiplayer_title: "Multiplayer Settings" # We'll be changing this around significantly soon. Until then, it's not important to translate. +# multiplayer_toggle: "Enable multiplayer" +# multiplayer_toggle_description: "Allow others to join your game." +# multiplayer_link_description: "Give this link to anyone to have them join you." +# multiplayer_hint_label: "Hint:" +# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." +# multiplayer_coming_soon: "More multiplayer features to come!" +# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." + +# legal: +# page_title: "Legal" +# opensource_intro: "CodeCombat is completely open source." +# opensource_description_prefix: "Check out " +# github_url: "our GitHub" +# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " +# archmage_wiki_url: "our Archmage wiki" +# opensource_description_suffix: "for a list of the software that makes this game possible." +# practices_title: "Respectful Best Practices" +# practices_description: "These are our promises to you, the player, in slightly less legalese." +# privacy_title: "Privacy" +# privacy_description: "We will not sell any of your personal information." +# security_title: "Security" +# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." +# email_title: "Email" +# email_description_prefix: "We will not inundate you with spam. Through" +# email_settings_url: "your email settings" +# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." +# cost_title: "Cost" +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." +# copyrights_title: "Copyrights and Licenses" +# contributor_title: "Contributor License Agreement" +# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" +# cla_url: "CLA" +# contributor_description_suffix: "to which you should agree before contributing." +# code_title: "Code - MIT" +# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" +# mit_license_url: "MIT license" +# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." +# art_title: "Art/Music - Creative Commons " +# art_description_prefix: "All common content is available under the" +# cc_license_url: "Creative Commons Attribution 4.0 International License" +# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" +# art_music: "Music" +# art_sound: "Sound" +# art_artwork: "Artwork" +# art_sprites: "Sprites" +# art_other: "Any and all other non-code creative works that are made available when creating Levels." +# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." +# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" +# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." +# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." +# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." +# rights_title: "Rights Reserved" +# rights_desc: "All rights are reserved for Levels themselves. This includes" +# rights_scripts: "Scripts" +# rights_unit: "Unit configuration" +# rights_description: "Description" +# rights_writings: "Writings" +# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." +# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." +# nutshell_title: "In a Nutshell" +# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." + +# ladder_prizes: +# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. +# blurb_1: "These prizes will be awarded according to" +# blurb_2: "the tournament rules" +# blurb_3: "to the top human and ogre players." +# blurb_4: "Two teams means double the prizes!" +# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" +# rank: "Rank" +# prizes: "Prizes" +# total_value: "Total Value" +# in_cash: "in cash" +# custom_wizard: "Custom CodeCombat Wizard" +# custom_avatar: "Custom CodeCombat avatar" +# heap: "for six months of \"Startup\" access" +# credits: "credits" +# one_month_coupon: "coupon: choose either Rails or HTML" +# one_month_discount: "discount, 30% off: choose either Rails or HTML" +# license: "license" +# oreilly: "ebook of your choice" + +# account_profile: +# settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. +# edit_profile: "Edit Profile" +# done_editing: "Done Editing" +# profile_for_prefix: "Profile for " +# profile_for_suffix: "" +# featured: "Featured" +# not_featured: "Not Featured" +# looking_for: "Looking for:" +# last_updated: "Last updated:" +# contact: "Contact" +# active: "Looking for interview offers now" +# inactive: "Not looking for offers right now" +# complete: "complete" +# next: "Next" +# next_city: "city?" +# next_country: "pick your country." +# next_name: "name?" +# next_short_description: "write a short description." +# next_long_description: "describe your desired position." +# next_skills: "list at least five skills." +# next_work: "chronicle your work history." +# next_education: "recount your educational ordeals." +# next_projects: "show off up to three projects you've worked on." +# next_links: "add any personal or social links." +# next_photo: "add an optional professional photo." +# next_active: "mark yourself open to offers to show up in searches." +# example_blog: "Blog" +# example_personal_site: "Personal Site" +# links_header: "Personal Links" +# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." +# links_name: "Link Name" +# links_name_help: "What are you linking to?" +# links_link_blurb: "Link URL" +# basics_header: "Update basic info" +# basics_active: "Open to Offers" +# basics_active_help: "Want interview offers right now?" +# basics_job_title: "Desired Job Title" +# basics_job_title_help: "What role are you looking for?" +# basics_city: "City" +# basics_city_help: "City you want to work in (or live in now)." +# basics_country: "Country" +# basics_country_help: "Country you want to work in (or live in now)." +# basics_visa: "US Work Status" +# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" +# basics_looking_for: "Looking For" +# basics_looking_for_full_time: "Full-time" +# basics_looking_for_part_time: "Part-time" +# basics_looking_for_remote: "Remote" +# basics_looking_for_contracting: "Contracting" +# basics_looking_for_internship: "Internship" +# basics_looking_for_help: "What kind of developer position do you want?" +# name_header: "Fill in your name" +# name_anonymous: "Anonymous Developer" +# name_help: "Name you want employers to see, like 'Nick Winter'." +# short_description_header: "Write a short description of yourself" +# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." +# short_description: "Tagline" +# short_description_help: "Who are you, and what are you looking for? 140 characters max." +# skills_header: "Skills" +# skills_help: "Tag relevant developer skills in order of proficiency." +# long_description_header: "Describe your desired position" +# long_description_blurb: "Tell employers how awesome you are and what role you want." +# long_description: "Self Description" +# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." +# work_experience: "Work Experience" +# work_header: "Chronicle your work history" +# work_years: "Years of Experience" +# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" +# work_blurb: "List your relevant work experience, most recent first." +# work_employer: "Employer" +# work_employer_help: "Name of your employer." +# work_role: "Job Title" +# work_role_help: "What was your job title or role?" +# work_duration: "Duration" +# work_duration_help: "When did you hold this gig?" +# work_description: "Description" +# work_description_help: "What did you do there? (140 chars; optional)" +# education: "Education" +# education_header: "Recount your academic ordeals" +# education_blurb: "List your academic ordeals." +# education_school: "School" +# education_school_help: "Name of your school." +# education_degree: "Degree" +# education_degree_help: "What was your degree and field of study?" +# education_duration: "Dates" +# education_duration_help: "When?" +# education_description: "Description" +# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" +# our_notes: "CodeCombat's Notes" +# remarks: "Remarks" +# projects: "Projects" +# projects_header: "Add 3 projects" +# projects_header_2: "Projects (Top 3)" +# projects_blurb: "Highlight your projects to amaze employers." +# project_name: "Project Name" +# project_name_help: "What was the project called?" +# project_description: "Description" +# project_description_help: "Briefly describe the project." +# project_picture: "Picture" +# project_picture_help: "Upload a 230x115px or larger image showing off the project." +# project_link: "Link" +# project_link_help: "Link to the project." +# player_code: "Player Code" + +# employers: +# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." +# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." +# hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. +# get_started: "Get Started" +# already_screened: "We've already technically screened all our candidates" +# filter_further: ", but you can also filter further:" +# filter_visa: "Visa" +# filter_visa_yes: "US Authorized" +# filter_visa_no: "Not Authorized" +# filter_education_top: "Top School" +# filter_education_other: "Other" +# filter_role_web_developer: "Web Developer" +# filter_role_software_developer: "Software Developer" +# filter_role_mobile_developer: "Mobile Developer" +# filter_experience: "Experience" +# filter_experience_senior: "Senior" +# filter_experience_junior: "Junior" +# filter_experience_recent_grad: "Recent Grad" +# filter_experience_student: "College Student" +# filter_results: "results" +# start_hiring: "Start hiring." +# reasons: "Three reasons you should hire through us:" +# everyone_looking: "Everyone here is looking for their next opportunity." +# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." +# weeding: "Sit back; we've done the weeding for you." +# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." +# pass_screen: "They will pass your technical screen." +# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." +# make_hiring_easier: "Make my hiring easier, please." +# what: "What is CodeCombat?" +# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." +# cost: "How much do we charge?" +# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." +# candidate_name: "Name" +# candidate_location: "Location" +# candidate_looking_for: "Looking For" +# candidate_role: "Role" +# candidate_top_skills: "Top Skills" +# candidate_years_experience: "Yrs Exp" +# candidate_last_updated: "Last Updated" +# candidate_who: "Who" +# featured_developers: "Featured Developers" +# other_developers: "Other Developers" +# inactive_developers: "Inactive Developers" + +# admin: +# av_espionage: "Espionage" # Really not important to translate /admin controls. +# av_espionage_placeholder: "Email or username" +# av_usersearch: "User Search" +# av_usersearch_placeholder: "Email, username, name, whatever" +# av_usersearch_search: "Search" +# av_title: "Admin Views" +# av_entities_sub_title: "Entities" +# av_entities_users_url: "Users" +# av_entities_active_instances_url: "Active Instances" +# av_entities_employer_list_url: "Employer List" +# av_entities_candidates_list_url: "Candidate List" +# av_entities_user_code_problems_list_url: "User Code Problems List" +# av_other_sub_title: "Other" +# av_other_debug_base_url: "Base (for debugging base.jade)" +# u_title: "User List" +# ucp_title: "User Code Problems" +# lg_title: "Latest Games" +# clas: "CLAs" diff --git a/app/locale/ms.coffee b/app/locale/ms.coffee index 6d52afcad..78f8572db 100644 --- a/app/locale/ms.coffee +++ b/app/locale/ms.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa slogan: "Belajar Kod bDengan Permainan" no_ie: "CodeCombat tidak berfungsi dalam Internet Explorer 8 dan terdahulu. Maaf!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat tidak dibangunkan untuk telefon mudah-alih dan tablet dan tidak akan berfungsi!" # Warning that shows up on mobile devices - play: "Mula" # The big play button that just starts playing a level + play: "Mula" # The big play button that opens up the campaign view. old_browser: "Uh oh, browser anda terlalu lama untuk CodeCombat berfungsi. Maaf!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Anda boleh mencuba, tapi mungkin ia tidak akan berfungsi." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "Buat Akaun" log_in: "Log Masuk" # logging_in: "Logging In" log_out: "Log Keluar" - recover: "Perbaharui Akaun" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" signup: -# create_account_title: "Create Account to Save Progress" - description: "Ianya percuma. Hanya berberapa langkah sahaja:" email_announcements: "Terima pengesahan melalui Emel" - coppa: "13+ atau bukan- USA" - coppa_why: "(Kenapa?)" creating: "Sedang membuat Akaun..." sign_up: "Daftar" log_in: "Log Masuk" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" recover: recover_account_title: "Dapatkan Kembali Akaun" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Pemuatan..." saving: "Menyimpan data..." sending: "Menghantar maklumat.." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # fork: "Fork" play: "Mula" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" general: and: "dan" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # date: "Date" # body: "Body" version: "Versi" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" commit_msg: "Mesej Commit" +# review: "Review" # version_history: "Version History" version_history_for: "Versi History untuk: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" result: "Keputusan" results: "Keputusan-keputusan" description: "Deskripsi" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Simpan versi baru" new_major_version: "Versi utama yang baru" +# submitting_patch: "Submitting Patch..." cla_prefix: "Untuk menyimpan pengubahsuaian, anda perlu setuju dengan" # cla_url: "CLA" # cla_suffix: "." cla_agree: "SAYA SETUJU" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Hubungi CodeCombat" welcome: "Kami gemar mendengar dari anda! Gunakan borang ini dan hantar emel kepada kami. " - contribute_prefix: "Jikalau anda berasa besar hati untuk menyumbang, sila lihat " - contribute_page: "laman kami untuk menyumbang" -# contribute_suffix: "!" forum_prefix: "Untuk perkara lain, sila cuba " forum_page: "forum kami" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Hantar Maklumbalas" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa autosave: "Pengubahsuaian disimpan secara automatik" me_tab: "Saya" picture_tab: "Gambar" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Kata-laluan" emails_tab: "Kesemua E-mel" # admin: "Admin" new_password: "Kata-laluan baru" new_password_verify: "Verifikasi" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." email_announcements: "Pengumuman" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" cost_description: "Buat masa ini, CodeCombat adalah 100% percuma! salah satu daripada tujuan kami adalah untuk membiarkan ia sebegitu, supaya ramai boleh bermain, di mana sahaja mereka berada. Jikalau langit menjadi gelap untuk kami, kami akan mengecaj untuk langganan atau untuk beberapa muatan, tapi kami lebih suka untuk tidak berbuat demikian. Jika kami bernasib baik, kami dapat menanggung syarikat kami dengan:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." copyrights_title: "Hakcipta dan Pemelesenan" contributor_title: "Persetujuan Lesen Penyumbang" contributor_description_prefix: "Kesemua sumbangan, termasuk di dalam laman dan di dalam repositiri GitHub, tertakluk kepada" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/no.coffee b/app/locale/my.coffee similarity index 63% rename from app/locale/no.coffee rename to app/locale/my.coffee index fae0aa831..3fdc2dc50 100644 --- a/app/locale/no.coffee +++ b/app/locale/my.coffee @@ -1,52 +1,53 @@ -module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", translation: - home: - slogan: "Lær å kode ved å spille et spill" - no_ie: "CodeCombat kjører ikke på IE8 eller eldre. Beklager!" # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat ble ikke designet for mobile enheter, og vil muligens ikke virke!" # Warning that shows up on mobile devices - play: "Spill" # The big play button that just starts playing a level +module.exports = nativeDescription: "မြန်မာစကား", englishDescription: "Myanmar language", translation: +# home: +# slogan: "Learn to Code by Playing a Game" +# no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older +# no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices +# play: "Play" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page # for_developers: "For Developers" # Not currently shown on home page. # or_ipad: "Or download for iPad" - nav: - play: "Spill" # The top nav bar entry where players choose which levels to play +# nav: +# play: "Levels" # The top nav bar entry where players choose which levels to play # community: "Community" - editor: "Editor" - blog: "Blogg" - forum: "Forum" +# editor: "Editor" +# blog: "Blog" +# forum: "Forum" # account: "Account" # profile: "Profile" # stats: "Stats" # code: "Code" - admin: "Administrator" # Only shows up when you are an admin - home: "Hovedside" - contribute: "Bidra" - legal: "Juridisk" - about: "Om" - contact: "Kontakt" - twitter_follow: "Følg" +# admin: "Admin" # Only shows up when you are an admin +# home: "Home" +# contribute: "Contribute" +# legal: "Legal" +# about: "About" +# contact: "Contact" +# twitter_follow: "Follow" # teachers: "Teachers" - modal: - close: "Lukk" - okay: "Ok" +# modal: +# close: "Close" +# okay: "Okay" - not_found: - page_not_found: "Finner ikke siden" +# not_found: +# page_not_found: "Page not found" diplomat_suggestion: - title: "Hjelp med oversettelse av CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. - sub_heading: "Vi trenger dine språkkunnskaper." - pitch_body: "Vi utvikler CodeCombat på engelsk, men vi vi har allerede spillere over hele verden. Mange av dem vil spille på norsk, men snakker ikke engelsk, så hvis du kan snakke begge språk, vennligst vurder å meld deg på som Diplomat og hjelp å oversette både CodeCombat web siden og alle nivåene til norsk." - missing_translations: "Inntil vi har oversatt alt til norsk vil du se engelsk hvor norsk ikke er tilgjengelig." - learn_more: "Lær mer om hvordan det er å være en Diplomat" - subscribe_as_diplomat: "Meld deg på som Diplomat" +# title: "Help translate CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. +# sub_heading: "We need your language skills." + pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Myanmar language but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Myanmar language." + missing_translations: "Until we can translate everything into Myanmar language, you'll see English when Myanmar language isn't available." +# learn_more: "Learn more about being a Diplomat" +# subscribe_as_diplomat: "Subscribe as a Diplomat" - play: +# play: # play_as: "Play As" # Ladder page # spectate: "Spectate" # Ladder page # players: "players" # Hover over a level on /play @@ -56,62 +57,60 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" - level_difficulty: "Vanskelighetsgrad: " - campaign_beginner: "Nybegynner brett" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# level_difficulty: "Difficulty: " +# campaign_beginner: "Beginner Campaign" +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Velg brett" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Du kan velge hvilket som helst brett under, eller diskutere brettene på " - adventurer_forum: "Eventyrerforumet" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... hvor du lærer trolldommen bak programmering." - campaign_dev: "Vanskeligere brett (tilfeldige)" - campaign_dev_description: "... hvor du lærer hvordan du bruker skjermbildene mens du stadig løser mer vanskelige utfordringer." - campaign_multiplayer: "Flerspiller brett (arenaer)" - campaign_multiplayer_description: "... hvor du spiller direkte mot andre spillere." - campaign_player_created: "Brett laget av andre brukere" - campaign_player_created_description: "... hvor du kjemper mot kreativiteten til en av dine medspillende <a href=\"/contribute#artisan\">Artisan Trollmenn</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# adjust_volume: "Adjust volume" +# campaign_multiplayer: "Multiplayer Arenas" +# campaign_multiplayer_description: "... in which you code head-to-head against other players." +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." - login: - sign_up: "Lag ny konto" - log_in: "Logg inn" +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" + +# login: +# sign_up: "Create Account" +# log_in: "Log In" # logging_in: "Logging In" - log_out: "Logg ut" - recover: "Gjenåpne konto" +# log_out: "Log Out" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" - signup: -# create_account_title: "Create Account to Save Progress" - description: "Det er gratis. Trenger bare litt informasjon så er du klar:" - email_announcements: "Motta nyhetsbrev på epost" - coppa: "over 13 år eller bor ikke i USA" - coppa_why: "(Hvorfor?)" - creating: "Oppretter konto..." - sign_up: "Registrer deg" - log_in: "Logg inn med passord" +# signup: +# email_announcements: "Receive announcements by email" +# creating: "Creating Account..." +# sign_up: "Sign Up" +# log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -126,40 +125,60 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # misc: "Misc" # books: "Books" - common: - loading: "Laster..." +# common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" +# loading: "Loading..." # saving: "Saving..." # sending: "Sending..." # send: "Send" - cancel: "Avbryt" +# cancel: "Cancel" # save: "Save" # publish: "Publish" # create: "Create" - manual: "Manuelt" +# manual: "Manual" # fork: "Fork" - play: "Spill" # When used as an action verb, like "Play next level" +# play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" - general: +# general: # and: "and" - name: "Navn" +# name: "Name" # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" - or: "eller" +# or: "or" # subject: "Subject" - email: "Epost" +# email: "Email" # password: "Password" - message: "Melding" +# message: "Message" # code: "Code" # ladder: "Ladder" # when: "When" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -191,61 +213,62 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # year: "year" # years: "years" - play_level: - done: "Ferdig" - home: "Hovedside" # Not used any more, will be removed soon. +# play_level: +# done: "Done" +# home: "Home" # Not used any more, will be removed soon. # level: "Level" # Like "Level: Dungeons of Kithgard" # skip: "Skip" # game_menu: "Game Menu" - guide: "Guide" - restart: "Start på nytt" - goals: "Mål" +# guide: "Guide" +# restart: "Restart" +# goals: "Goals" # goal: "Goal" # running: "Running..." # success: "Success!" # incomplete: "Incomplete" # timed_out: "Ran out of time" # failing: "Failing" - action_timeline: "Hendelsestidslinje" - click_to_select: "Klikk på en enhet for å velge den." +# action_timeline: "Action Timeline" +# click_to_select: "Click on a unit to select it." # control_bar_multiplayer: "Multiplayer" # control_bar_join_game: "Join Game" # reload: "Reload" - reload_title: "Laste all koden på nytt?" - reload_really: "Er du sikker på at du vil laste dette nivået på nytt, tilbake til begynnelsen?" - reload_confirm: "Last alle på nytt" +# reload_title: "Reload All Code?" +# reload_really: "Are you sure you want to reload this level back to the beginning?" +# reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" - victory_title_suffix: " Ferdig" - victory_sign_up: "Logg deg inn for oppdateringer" - victory_sign_up_poke: "Vil du ha siste nytt på epost? Opprett en gratis konto, så vil vi holde deg oppdatert!" - victory_rate_the_level: "Bedøm nivået: " # Only in old-style levels. +# victory_title_suffix: " Complete" +# victory_sign_up: "Sign Up to Save Progress" +# victory_sign_up_poke: "Want to save your code? Create a free account!" +# victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Spill neste nivå" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" - victory_go_home: "Gå til Hovedsiden" # Only in old-style levels. - victory_review: "Fortell oss mer!" # Only in old-style levels. - victory_hour_of_code_done: "Er du ferdig?" - victory_hour_of_code_done_yes: "Ja, jeg er ferdig med min time i koding!" - guide_title: "Guide" - tome_minion_spells: "Din Minions' Trylleformularer" # Only in old-style levels. - tome_read_only_spells: "Kun-lesbare trylleformularer" # Only in old-style levels. - tome_other_units: "Andre enheter" # Only in old-style levels. +# victory_go_home: "Go Home" # Only in old-style levels. +# victory_review: "Tell us more!" # Only in old-style levels. +# victory_hour_of_code_done: "Are You Done?" +# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" +# guide_title: "Guide" +# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. +# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. +# tome_other_units: "Other Units" # Only in old-style levels. # tome_cast_button_run: "Run" # tome_cast_button_running: "Running" # tome_cast_button_ran: "Ran" # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). - tome_select_a_thang: "Velg noe for å " - tome_available_spells: "Tilgjenglige trylleformularer" +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). +# tome_select_a_thang: "Select Someone for " +# tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,14 +319,34 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Tilpass trollmann" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." - game_menu: +# game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" - multiplayer_tab: "Flerspiller" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" +# multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" # choose_hero_caption: "Choose hero, language" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,54 +562,155 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." - contact: - contact_us: "Kontakt CodeCombat" - welcome: "Kontakt oss gjerne, men vi må ha meldingen på engelsk! Bruk dette skjemaet for å sende oss en epost." - contribute_prefix: "Hvis du er interessert i å bidra, sjekk ut vår " - contribute_page: "bidragsside" - contribute_suffix: "!" - forum_prefix: "Du kan også stille spørsmål i våre åpne " - forum_page: "diskusjonsgrupper" - forum_suffix: " om du ønsker det. For å få flest mulig svar er det lurt å skrive på engelsk" - send: "Send tilbakemelding" +# contact: +# contact_us: "Contact CodeCombat" +# welcome: "Good to hear from you! Use this form to send us email. " +# forum_prefix: "For anything public, please try " +# forum_page: "our forum" +# forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" +# send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated - account_settings: - title: "Kontoinnstillinger" - not_logged_in: "Logg inn eller opprett en konto for å endre innstillingene dine." - autosave: "Endringer lagres automatisk" - me_tab: "Meg" - picture_tab: "Bilde" +# account_settings: +# title: "Account Settings" +# not_logged_in: "Log in or create an account to change your settings." +# autosave: "Changes Save Automatically" +# me_tab: "Me" +# picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" - password_tab: "Passord" - emails_tab: "Epost" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" +# password_tab: "Password" +# emails_tab: "Emails" # admin: "Admin" - new_password: "Nytt passord" - new_password_verify: "Verifiser" - email_subscriptions: "Epostabonnement" +# new_password: "New Password" +# new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." +# email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." - email_announcements: "Kunngjøringer" - email_announcements_description: "Få epost om siste nytt og utvikling fra CodeCombat." +# email_announcements: "Announcements" +# email_announcements_description: "Get emails on the latest news and developments at CodeCombat." # email_notifications: "Notifications" # email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." # email_any_notes: "Any Notifications" @@ -467,27 +718,26 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # email_news: "News" # email_recruit_notes: "Job Opportunities" # email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." - contributor_emails: "Bidragsyterklasse epost" - contribute_prefix: "Vi leter etter folk som vil delta på kodefesten vår! Sjekk ut " - contribute_page: "bidragssiden" - contribute_suffix: " for å finne ut mer." - email_toggle: "Vis alle" - error_saving: "Lagring feilet" - saved: "Endringer lagret" - password_mismatch: "Passordene er ikke like." +# contributor_emails: "Contributor Class Emails" +# contribute_prefix: "We're looking for people to join our party! Check out the " +# contribute_page: "contribute page" +# contribute_suffix: " to find out more." +# email_toggle: "Toggle All" +# error_saving: "Error Saving" +# saved: "Changes Saved" +# password_mismatch: "Password does not match." # password_repeat: "Please repeat your password." # job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated # job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" - wizard_tab: "Trollmann" - wizard_color: "Farge på trollmannens klær" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -857,19 +1221,19 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # guide: # temp: "Temp" - multiplayer: - multiplayer_title: "Flerspillerinnstillinger" # We'll be changing this around significantly soon. Until then, it's not important to translate. +# multiplayer: +# multiplayer_title: "Multiplayer Settings" # We'll be changing this around significantly soon. Until then, it's not important to translate. # multiplayer_toggle: "Enable multiplayer" # multiplayer_toggle_description: "Allow others to join your game." - multiplayer_link_description: "Gi denne lenken til de du vil spille med." - multiplayer_hint_label: "Hint:" - multiplayer_hint: " Klikk lenken for å velge alle, så trykker du Apple-C eller Ctrl-C for å kopiere lenken." - multiplayer_coming_soon: "Det kommer flere flerspillsmuligheter!" +# multiplayer_link_description: "Give this link to anyone to have them join you." +# multiplayer_hint_label: "Hint:" +# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." +# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,27 +1306,11 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - - account_profile: +# account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" # done_editing: "Done Editing" - profile_for_prefix: "Profil for " +# profile_for_prefix: "Profile for " # profile_for_suffix: "" # featured: "Featured" # not_featured: "Not Featured" diff --git a/app/locale/nb.coffee b/app/locale/nb.coffee index 33de9abc1..1299f9e99 100644 --- a/app/locale/nb.coffee +++ b/app/locale/nb.coffee @@ -3,10 +3,11 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg slogan: "Lær å kode ved å spille et spill" no_ie: "CodeCombat kjører ikke på IE8 eller eldre. Beklager!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat ble ikke designet for mobile enheter, og vil kanskje ikke virke!" # Warning that shows up on mobile devices - play: "Spill" # The big play button that just starts playing a level + play: "Spill" # The big play button that opens up the campaign view. old_browser: "Å nei, nettleseren din er for gammel til å kjøre CodeCombat. Beklager!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Du kan prøve likevel, men det vil kanskje ikke virke." -# campaign: "Campaign" + ipad_browser: "Dårlige nyheter: CodeCombat kjører ikke i nettleseren på iPad. Gode nyheter: iPad appen vår venter bare på godkjenning av Apple." + campaign: "Kampanje" for_beginners: "For nybegynnere" multiplayer: "Flerspiller" # Not currently shown on home page for_developers: "For Utviklere" # Not currently shown on home page. @@ -20,7 +21,7 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg forum: "Forum" account: "Konto" profile: "Profil" -# stats: "Stats" + stats: "Statistikk" code: "Kode" admin: "Administrator" # Only shows up when you are an admin home: "Hjem" @@ -49,84 +50,84 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg play: play_as: "Spill som" # Ladder page spectate: "Vær tilskuer" # Ladder page - players: "spillere" # Hover over a level on /play + players: "Spillere" # Hover over a level on /play hours_played: "Timer spilt" # Hover over a level on /play items: "Utstyr" # Tooltip on item shop button from /play unlock: "Lås opp" # For purchasing items and heroes confirm: "Bekreft" - owned: "Eid" #"Owned" # For items you own + owned: "Eid" # For items you own locked: "Låst" + purchasable: "Kan kjøpes" # For a hero you unlocked but haven't purchased available: "Tilgjengelig" -# skills_granted: "Skills Granted" # Property documentation details + skills_granted: "Gir Evnene" # Property documentation details heroes: "Helter" # Tooltip on hero shop button from /play achievements: "Prestasjoner" # Tooltip on achievement list button from /play - account: "Bruker" # Tooltip on account button from /play + account: "Konto" # Tooltip on account button from /play settings: "Innstillinger" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play next: "Neste" # Go from choose hero to choose inventory before playing a level change_hero: "Bytt Helt" # Go back from choose inventory to choose hero - choose_inventory: "Ta i bruk gjenstander" # "Equip Items" + choose_inventory: "Ta i bruk gjenstander" buy_gems: "Kjøp Juveler" - older_campaigns: "Gamle felttog" # "Older Campaigns" + subscription_required: "Krever abonnement" anonymous: "Anonym Spiller" level_difficulty: "Vanskelighetsgrad: " campaign_beginner: "Begynner Felttog" - awaiting_levels_adventurer_prefix: "Vi lanserer fem nye brett hver uke" #"We release five levels per week." - awaiting_levels_adventurer: "Registrer deg som Eventyrer" #"Sign up as an Adventurer" - awaiting_levels_adventurer_suffix: "for å få spille nye brett før alle andre." #"to be the first to play new levels." - choose_your_level: "Velg Ditt Nivå" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Du kan hoppe til hvilket som helst brett under, eller diskutere nivåene på " - adventurer_forum: "Eventyrer forumet" - adventurer_suffix: "." - campaign_old_beginner: "Gammel Nybegynner Kampanje" #"Old Beginner Campaign" - campaign_old_beginner_description: "... hvor du lærer magien bak programmering." - campaign_dev: "Noen Litt Vanskeligere" - campaign_dev_description: "... hvor du lærer grensesnittet mens du gjør litt vanskeligere oppgaver." - campaign_multiplayer: "Flerspiller Arenaer" + awaiting_levels_adventurer_prefix: "Vi lanserer fem nye brett hver uke" # {change} + awaiting_levels_adventurer: "Registrer deg som Eventyrer" + awaiting_levels_adventurer_suffix: "for å få spille nye brett før alle andre." + adjust_volume: "Juster lydnivå" + campaign_multiplayer: "Flerspillerarenaer" campaign_multiplayer_description: "... hvor du spiller direkte mot andre spillere." - campaign_player_created: "Laget Av Spillere" - campaign_player_created_description: "... hvor du kjemper mot kreativiteten til en av dine medspillende <a href=\"/contribute#artisan\">Artisan Trollmenn</a>." #FIXME - campaign_classic_algorithms: "Klassiske Algoritmer" - campaign_classic_algorithms_description: "... hvor du lærer noen av de mest populære algoritmene innen programmering" #"... in which you learn the most popular algorithms in Computer Science." - campaign_forest: "Skogsfelttog" # "Forest Campaign" - campaign_dungeon: "Katakombefelttog" # "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Du gjør fantastisk fremgang! Fortell noen hvor mye du har lært gjennom CodeCombat." + email_invalid: "E-postadressen er ugyldig." + form_blurb: "Skriv ned e-postadressen deres nedenfor, så skal vi vise dem!" + form_label: "E-postadresse" + placeholder: "E-postadresse" + title: "Godt jobbet, lærling" login: sign_up: "Lag konto" log_in: "Logg inn" logging_in: "Logger inn" log_out: "Logg ut" - recover: "glemt passord" + forgot_password: "Glemt passord?" authenticate_gplus: "Autentiser G+" load_profile: "Last G+ Profil" - load_email: "Last G+ Epost" finishing: "Fullfører" + sign_in_with_facebook: "Logg på med Facebook" + sign_in_with_gplus: "Logg på med G+" + signup_switch: "Vil du lage en konto?" signup: - create_account_title: "Opprett konto for å lagre fremgang" - description: "Det er gratis. Vi trenger bare noen få detaljer, så er du klar:" email_announcements: "Motta kunngjøringer på epost" - coppa: "13+ år eller ikke fra USA" - coppa_why: "(Hvorfor?)" creating: "Oppretter Konto..." sign_up: "Lag konto" log_in: "logg inn med passord" social_signup: "Eller du kan registrere deg med Facebook eller Google+:" - required: "Du må være logget inn for å gå dit." #"You need to log in before you can go that way." + required: "Du må være logget inn for å gå dit." + login_switch: "Har du allerede en konto?" recover: - recover_account_title: "Tilbakestill Passord" #"Recover Account" - send_password: "Send nytt passord" #"Send Recovery Password" - recovery_sent: "Epost sendt." #"Recovery email sent." + recover_account_title: "Tilbakestill Passord" + send_password: "Send nytt passord" + recovery_sent: "Epost sendt." items: - primary: "Første" # "Primary" - secondary: "Andre" # "Secondary" + primary: "Første" + secondary: "Andre" armor: "Rustning" accessories: "Tilbehør" misc: "Diverse" books: "Bøker" common: + back: "Tilbake" # When used as an action verb, like "Navigate backward" + continue: "Fortsett" # When used as an action verb, like "Continue forward" loading: "Laster..." saving: "Lagrer..." sending: "Sender..." @@ -136,12 +137,17 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg publish: "Publiser" create: "Opprett" manual: "Manuelt" -# fork: "Fork" + fork: "Forgrening" play: "Spill" # When used as an action verb, like "Play next level" retry: "Prøv igjen" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + actions: "Handlinger" + info: "Informasjon" + help: "Hjelp" + watch: "Se på" + unwatch: "Ikke se på" + submit_patch: "Lever en Patch" + submit_changes: "Lever Endringer" +# save_changes: "Save Changes" general: and: "og" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg date: "Dato" body: "Kropp" version: "Versjon" + pending: "Avventer" + accepted: "Akseptert" + rejected: "Avvist" + withdrawn: "Trukket tilbake" + submitter: "Innsender" + submitted: "Levert" commit_msg: "Commit-melding" + review: "Gjennomgang" version_history: "Versjonshistorikk" version_history_for: "Versjonshistorikk for: " + select_changes: "Velg to endringer under for å se forskjellene." + undo_prefix: "Angre" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Gjenta" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Spill av forhåndsvisning av valgt brett" result: "Resultat" results: "Resultater" description: "Beskrivelse" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg hard: "Vanskelig" player: "Spiller" player_level: "Nivå" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Kriger" + ranger: "Vokter" + wizard: "Trollmann" units: second: "sekund" @@ -202,133 +224,246 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg goals: "Mål" goal: "Mål" running: "Kjører..." - success: "Vellykket!" + success: "Suksess!" incomplete: "Ufullstendig" timed_out: "Tiden er ute" - failing: "Mislykkes" #"Failing" + failing: "Mislykkes" action_timeline: "Hendelsestidslinje" click_to_select: "Klikk på en enhet for å velge den." control_bar_multiplayer: "Flerspiller" -# control_bar_join_game: "Join Game" -# reload: "Reload" - reload_title: "Laste all koden på nytt?" - reload_really: "Er du sikker på at du vil laste dette nivået på nytt, tilbake til begynnelsen?" - reload_confirm: "Last Alle på Nytt" + control_bar_join_game: "Bli med i spillet" + reload: "Tilbakestill" + reload_title: "Tilbakestille all koden?" + reload_really: "Er du sikker på at du vil tilbakestille dette brettet til begynnelsen?" + reload_confirm: "Tilbakestill alt" + victory: "Seier" victory_title_prefix: "" victory_title_suffix: " Ferdig" - victory_sign_up: "Tegn deg på for Oppdateringer" #FIXME + victory_sign_up: "Tegn deg på for Oppdateringer" victory_sign_up_poke: "Vil du ha siste nytt på epost? Opprett en gratis konto, så vil vi holde deg oppdatert!" victory_rate_the_level: "Bedøm nivået: " # Only in old-style levels. victory_return_to_ladder: "Tilbake til stige" victory_play_continue: "Fortsett" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Spill neste brett" - victory_play_more_practice: "Mer trening" - victory_play_too_easy: "For lett" - victory_play_just_right: "Passe vanskelig" - victory_play_too_hard: "For vanskelig" victory_saving_progress: "Lagrer framskritt" victory_go_home: "Gå Hjem" # Only in old-style levels. victory_review: "Fortell oss mer!" # Only in old-style levels. victory_hour_of_code_done: "Er du ferdig?" victory_hour_of_code_done_yes: "Ja, jeg er ferdig med min Kodetime!" + victory_experience_gained: "XP Opparbeidet" + victory_gems_gained: "Mottatte juveler" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Guide" - tome_minion_spells: "Din Minions' Trylleformularer" # Only in old-style levels. #FIXME + tome_minion_spells: "Din Minions' Trylleformularer" # Only in old-style levels. tome_read_only_spells: "Kun-Lesbare Trylleformularer" # Only in old-style levels. tome_other_units: "Andre Enheter" # Only in old-style levels. tome_cast_button_run: "Kjør" tome_cast_button_running: "Kjører" tome_cast_button_ran: "Kjørte" tome_submit_button: "Send inn" - tome_reload_method: "Tilbakestill denne metoden til den orginale koden" #"Reload original code for this method" # Title text for individual method reload button. + tome_reload_method: "Tilbakestill denne metoden til den orginale koden" # Title text for individual method reload button. tome_select_method: "Velg en metode" - tome_see_all_methods: "Se alle metodene du kan redigere" # Title text for method list selector (shown when there are multiple programmable methdos). - tome_select_a_thang: "Velg Noe for å " #FIXME + tome_see_all_methods: "Se alle metodene du kan redigere" # Title text for method list selector (shown when there are multiple programmable methods). + tome_select_a_thang: "Velg Noe for å " tome_available_spells: "Tilgjenglige Trylleformularer" - tome_your_skills: "Dine Ferdigheter" #"Your Skills" - tome_current_method: "Gjeldende Metode" #"Current Method" + tome_your_skills: "Dine Ferdigheter" + tome_help: "Hjelp" + tome_current_method: "Gjeldende Metode" hud_continue_short: "Fortsett" code_saved: "Kode lagret" - skip_tutorial: "Hopp over (esc)" #"Skip (esc)" + skip_tutorial: "Hopp over (esc)" keyboard_shortcuts: "Hurtigtaster" - loading_ready: "Klar!" #"Ready!" - loading_start: "Start Brett" #"Start Level" + loading_ready: "Klar!" + loading_start: "Start Brett" problem_alert_title: "Fiks koden din" + problem_alert_help: "Hjelp" time_current: "Nå:" time_total: "Maks:" time_goto: "Gå til:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Prøv igjen" - infinite_loop_reset_level: "Tilbakestill Brett" #"Reset Level" + infinite_loop_reset_level: "Tilbakestill Brett" infinite_loop_comment_out: "Kommenter ut koden min" - tip_toggle_play: "Ctrl+P er play/pause" #"Toggle play/paused with Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ and Ctrl+] spoler bakover og fremover på tidslinjen." #"Ctrl+[ and Ctrl+] rewind and fast-forward." - tip_guide_exists: "Sjekk Guiden i spillmenyen på toppen av siden for nyttig informasjon." #"Click the guide, inside game menu (at the top of the page), for useful info." + tip_toggle_play: "Ctrl+P er play/pause" + tip_scrub_shortcut: "Ctrl+[ and Ctrl+] spoler bakover og fremover på tidslinjen." # {change} + tip_guide_exists: "Sjekk Guiden i spillmenyen på toppen av siden for nyttig informasjon." tip_open_source: "CodeCombat er 100% åpen kildekode!" - tip_beta_launch: "CodeCombat ble lansert i betautgave i oktober 2013." #"CodeCombat launched its beta in October, 2013." +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat ble lansert i betautgave i oktober 2013." tip_think_solution: "Tenk på løsningen, ikke på problemet." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" - tip_forums: "Stikk innom forumene og fortell oss hva du synes!" #"Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." - tip_all_species: "Vi tror på like muligheter til å lære programmering for alle arter." #"We believe in equal opportunities to learn programming for all species." -# tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." - tip_munchkin: "Hvis du ikke spiser grønnsakene dine kommer en munchkin og tar deg når du sover" # "If you don't eat your vegetables, a munchkin will come after you while you're asleep." + tip_theory_practice: "I teorien er det ikke forskjell på teori og praksis, men i praksis er det. - Yogi Berra" + tip_error_free: "Det er to måter å skrive feilfrie programmer på; bare den tredje virker. - Alan Perlis" + tip_debugging_program: "Hvis debugging er prosessen for å fjerne feil, så må programmering være prosessen for å legge dem inn. - Edsger W. Dijkstra" + tip_forums: "Stikk innom forumene og fortell oss hva du synes!" + tip_baby_coders: "I fremtiden vil selv babyer være Archmager." + tip_morale_improves: "Lastingen vil fortsette til moralen forbedres." + tip_all_species: "Vi tror på like muligheter til å lære programmering for alle arter." + tip_reticulating: "Fletter sammen ryggraden." + tip_harry: "Du er en trollmann, " + tip_great_responsibility: "Med gode programmeringsegenskaper følger det stort debuggingsansvar." + tip_munchkin: "Hvis du ikke spiser grønnsakene dine kommer en munchkin og tar deg når du sover" tip_binary: "Det finnes 10 typer mennesker i verden: de som forstår binærtall, og de som ikke gjør det." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." -# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Tilpass Trollmann" + tip_commitment_yoda: "En programmerer må ha det dypeste engasjement, den mest seriøse sinn. ~ Yoda" + tip_no_try: "Gjør. Eller ikke gjør. Å forsøke finnes ikke. - Yoda" + tip_patience: "Tålmodighet du må ha, unge Padawan. - Yoda" + tip_documented_bug: "En dokumentert feil er ingen feil, det er en funksjon." + tip_impossible: "Det virker alltid umulig før det er gjort. - Nelson Mandela" + tip_talk_is_cheap: "Snakk er billig. Vis meg koden. - Linus Torvalds" + tip_first_language: "Den mest katastrofale tingen du noen sinne kan lære er ditt første programmeringsspråk. - Alan Kay" + tip_hardware_problem: "Q: Hvor mange programmerere trengs det for å skifte en lyspære? A: Inge, det er et hardware problem." + tip_hofstadters_law: "Hofstadters Lov: Ting tar alltid lenger tid enn du tror, selv når du tar Hofstadters Lov med i beregningen." + tip_premature_optimization: "For tidlig optimalisering er roten til alt ondt - Donald Knuth" + tip_brute_force: "Når du er i tvil, bruk brute force - Ken Thompson" + tip_extrapolation: "Det finnes to typer mennesker: de som kan utlede ufullstendig data..." + tip_superpower: "Koding er det nærmeste vi kommer superkrefter." + tip_control_destiny: "I ekte åpen kildekode, har du rett til å kontrollere din egen skjebne. - Linus Torvalds" + tip_no_code: "Ingen kode er raskere enn ingen kode." + tip_code_never_lies: "Kode lyver aldri, kommentarer gjør det i blandt. — Ron Jeffries" + tip_reusable_software: "Før programvare kan være gjenbrukbart, må det først være brukbart." + tip_optimization_operator: "Hvert språk har en optimaliseringsoperator. I de fleste språk er den operatøren ‘//’" + tip_lines_of_code: "Å måle programmeringsfremgang ved hjelp av antall linjer kode er som å måle flybyggingsfremgang ved hjelp av vekt. - Bill Gates" + tip_source_code: "Jeg vil forandre verden, men de vil ikke gi meg kildekoden." + tip_javascript_java: "Java er for JavaScript, det bil er for teppe - Chris Heilman" + tip_move_forward: "Uansett hva du gjør, beveg deg fremover. - Martin Luther King Jr." + tip_google: "Har du et problem du ikke kan løse? Google det!" + tip_adding_evil: "Legger til en klype ondskap!" +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: - inventory_tab: "Utstyr" #"Inventory" + inventory_tab: "Utstyr" save_load_tab: "Lagre/Laste" options_tab: "Innstillinger" guide_tab: "Guide" -# guide_tab: "Guide" + guide_video_tutorial: "Videoveiledning" + guide_tips: "Tips" multiplayer_tab: "Flerspiller" - auth_tab: "Lag konto" #"Sign Up" - inventory_caption: "Utstyr helten din" #"Equip your hero" + auth_tab: "Lag konto" + inventory_caption: "Utstyr helten din" choose_hero_caption: "Velg helt og språk" save_load_caption: "... og se historikk" options_caption: "Velg innstillinger" guide_caption: "Dokumentasjon og tips" - multiplayer_caption: "Spill med venner" #"Play with friends!" -# auth_caption: "Save your progress." + multiplayer_caption: "Spill med venner" + auth_caption: "Lagre din fremgang." + + leaderboard: + leaderboard: "Topplisten" + view_other_solutions: "Sen andre løsninger" # {change} + scores: "Poeng" +# top_players: "Top Players by" + day: "I dag" + week: "Denne uken" + all: "All tid" + time: "Tid" + damage_taken: "Skade tatt" + damage_dealt: "Skade gitt" + difficulty: "Vanskelighetsgrad" + gold_collected: "Gull samlet" inventory: choose_inventory: "Velg utstyr" - equipped_item: "I Bruk" #"Equipped" - available_item: "Tilgjengelig" #"Available" + equipped_item: "I Bruk" + required_purchase_title: "Krav" + available_item: "Tilgjengelig" restricted_title: "Ikke tilgjengelig" - should_equip: "(dobbel-klikk for å bruke)" #"(double-click to equip)" - equipped: "(i bruk)" #"(equipped)" - locked: "(låst)" #"(locked)" - restricted: "(Ikke tilgjengelig i dette nivået)" - equip: "Bruk" #"Equip" - unequip: "Ta av" + should_equip: "(dobbel-klikk for å bruke)" + equipped: "(i bruk)" + locked: "(låst)" + restricted: "(Ikke tilgjengelig på dette brettet)" + equip: "Bruk" + unequip: "Ikke bruk" buy_gems: -# few_gems: "A few gems" few_gems: "Noen få juveler" -# pile_gems: "Pile of gems" pile_gems: "En haug juveler" -# chest_gems: "Chest of gems" chest_gems: "Kiste med juveler" purchasing: "Kjøper..." declined: "Kortet ditt ble avvist" retrying: "Server-feil, prøver igjen." + prompt_title: "Ikke nok juveler" + prompt_body: "Har du lyst på flere?" + prompt_button: "Til Butikken" + recovered: "Tidligere juvelkjøp hentet. Vennligst last siden på nytt" +# price: "x3500 / mo" + + subscribe: + comparison_blurb: "Spiss dine kunnskaper med et CodeCombat abonnement!" + feature1: "60+ grunnleggende brett fordelt på 4 verdener" # {change} + feature2: "7 kraftfulle <strong>nye helter</strong> med unike ferdigheter!" # {change} + feature3: "30+ bonusbrett" # {change} + feature4: "<strong>3500 bonusjuveler</strong> hver måned!" + feature5: "Videoveiledninger" + feature6: "Premium e-poststøtte" +# feature7: "Private <strong>Clans</strong>" + free: "Gratis" + month: "måned" + subscribe_title: "Abonnér" + unsubscribe: "Si opp abonnement" + confirm_unsubscribe: "Bekreft oppsigelse" + never_mind: "Ikke tenk på det, jeg elsker deg for det" + thank_you_months_prefix: "Takk for at du støttet oss de siste" + thank_you_months_suffix: "månedene." + thank_you: "Takk for at du støttet CodeCombat." + sorry_to_see_you_go: "Leit at du forlater oss! Vennligst gi oss beskjed om hva vi kunne gjort bedre." + unsubscribe_feedback_placeholder: "O, hva har vi gjort?" + parent_button: "Spør din forelder" + parent_email_description: "Vi vil sende de e-post, slik at de kan kjøpe et CodeCombat abonnement til deg." + parent_email_input_invalid: "E-postadressen er ugyldig." + parent_email_input_label: "Forelders e-postadresse" + parent_email_input_placeholder: "Skriv forelders e-postadresse" + parent_email_send: "Send e-post" + parent_email_sent: "E-post sendt!" + parent_email_title: "Hva er din forelders e-postadresse?" + parents: "For foreldre" + parents_title: "Barnet ditt vil lære å kode" # {change} + parents_blurb1: "Med CodeCombat vil dine barn lære seg å skrive ekte kode. De begynner med enkle kommandoer og går videre til mer avanserte emner." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "For USD 9.99 pr mnd, vil de få nye utfordringer hver uke og personlig e-poststøtte fra profesjonelle programmerere." # {change} + parents_blurb3: "Ingen risiko: 100% pengene tilbake-garanti, kun et klikk for å si opp abonnementet." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "Månedlig abonnement" + subscription_required_to_play: "Du trenger abonnement for å spille dette nivået." + unlock_help_videos: "Abonnér for å låse opp alle videoveiledningene." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: choose_hero: "Velg Din Helt" @@ -336,115 +471,220 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg programming_language_description: "Hvilket programmeringsspråk vil du bruke??" default: "Standard" experimental: "Eksperimentelt" - python_blurb: "Enkelt, men kraftig. Bra for både nybegynnere og eksperter." #"Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" + python_blurb: "Enkelt, men kraftig. Bra for både nybegynnere og eksperter." + javascript_blurb: "Språket som brukes på websider. (Ikke det samme som Java.)" coffeescript_blurb: "Hyggeligere JavaScript syntaks." clojure_blurb: "En moderne Lisp." - lua_blurb: "Et skriptspråk for spill." #"Game scripting language." - io_blurb: "Enkelt, men obskurt." #"Simple but obscure." -# status: "Status" + lua_blurb: "Et skriptspråk for spill." + io_blurb: "Enkelt, men obskurt." + status: "Status" +# hero_type: "Type" weapons: "Våpen" weapons_warrior: "Sverd - Kort rekkevidde, ingen magi" weapons_ranger: "Armbrøst, Gevær - Lang rekkevidde, ingen magi" weapons_wizard: "Tryllestav, Tryllestokk - Lang rekkevidde, magi" - attack: "Angrep" # Can also translate as "Attack" (was "Damage") + attack: "Angrep" # Can also translate as "Attack" health: "Helse" speed: "Fart" regeneration: "Regenerering" range: "Rekkevidde" # As in "attack or visual range" blocks: "Blokkerer" # As in "this shield blocks this much damage" - skills: "Ferdigheter" #"Skills" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" + skills: "Ferdigheter" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." + available_for_purchase: "Kan Kjøpes" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Låses opp på nivå:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Bare noen helter kan spille dette brettet." skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this - read_only: "kun-lese" #"read-only" + writable: "skrivbar" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "skrivebeskyttet" action_name: "navn" -# action_cooldown: "Takes" - action_specific_cooldown: "Gjenopplading" #"Cooldown" + action_cooldown: "Tar" + action_specific_cooldown: "Gjenopplading" action_damage: "Skade" action_range: "Rekkevidde" -# action_radius: "Radius" + action_radius: "Radius" action_duration: "Varighet" example: "Eksempel" ex: "f.eks." # Abbreviation of "example" current_value: "Nåværende verdi" - default_value: "Standard verdi" #"Default value" + default_value: "Standard verdi" parameters: "Parametere" returns: "Returnerer" - granted_by: "Gitt av" #"Granted by" + granted_by: "Gitt av" save_load: granularity_saved_games: "Lagret" granularity_change_history: "Historikk" options: - general_options: "Generelle innstillinger" #"General Options" # Check out the Options tab in the Game Menu while playing a level + general_options: "Generelle innstillinger" # Check out the Options tab in the Game Menu while playing a level volume_label: "Volum" music_label: "Musikk" music_description: "Bakgrunnsmusikk på/av." - autorun_label: "Auto-kjør" - autorun_description: "Kontroller automatisk kode-kjøring." editor_config: "Editor Oppsett" editor_config_title: "Editor Oppsett" editor_config_level_language_label: "Språk for dette brettet" editor_config_level_language_description: "Definer programmeringsspråk for dette brettet." - editor_config_default_language_label: "Standard programmeringsspråk" #"Default Programming Language" + editor_config_default_language_label: "Standard programmeringsspråk" editor_config_default_language_description: "Velg det programmeringsspråket du vil kode i når du starter på nye brett." editor_config_keybindings_label: "Hurtigtaster" - editor_config_keybindings_default: "Standard (Ace)" #"Default (Ace)" - editor_config_keybindings_description: "Legger til ekstra hurtigtaster kjent fra vanlige editorer." #"Adds additional shortcuts known from the common editors." - editor_config_livecompletion_label: "Autofullfør" #"Live Autocompletion" - editor_config_livecompletion_description: "Viser forslag til autofullføring av tekst mens du skriver" #"Displays autocomplete suggestions while typing." + editor_config_keybindings_default: "Standard (Ace)" + editor_config_keybindings_description: "Legger til ekstra hurtigtaster kjent fra vanlige editorer." + editor_config_livecompletion_label: "Autofullfør" + editor_config_livecompletion_description: "Viser forslag til autofullføring av tekst mens du skriver" editor_config_invisibles_label: "Vis usynlige tegn" editor_config_invisibles_description: "Viser usynlige tegn som mellomrom eller tab." - editor_config_indentguides_label: "Vis innrykkslinjer" #"Show Indent Guides" - editor_config_indentguides_description: "Viser vertikale linjer for å se antall innrykk bedre." #"Displays vertical lines to see indentation better." - editor_config_behaviors_label: "Smart oppførsel" #"Smart Behaviors" - editor_config_behaviors_description: "Autofullfører parenteser, krøllparenteser, hakeparenteser og anførselstegn" #"Autocompletes brackets, braces, and quotes." + editor_config_indentguides_label: "Vis innrykkslinjer" + editor_config_indentguides_description: "Viser vertikale linjer for å se antall innrykk bedre." + editor_config_behaviors_label: "Smart oppførsel" + editor_config_behaviors_description: "Autofullfører parenteser, krøllparenteser, hakeparenteser og anførselstegn" about: why_codecombat: "Hvorfor CodeCombat?" why_paragraph_1: "Hvis du vil lære å programmere trenger du ikke å ta timer. Du trenger å skrive masse kode og ha det kjempegøy mens du gjør det." why_paragraph_2_prefix: "Det er det programmering handler om. Det må være gøy. Ikke gøy som i" - why_paragraph_2_italic: "jippi et merke" #"yay a badge" - why_paragraph_2_center: "men gøy som i" #"but fun like" - why_paragraph_2_italic_caps: "NEI MOR JEG MÅ FULLFØRE DETTE BRETTET!" - why_paragraph_2_suffix: "Det er derfor CodeCombat er et flerspiller spill, ikke et spillaktig programmeringskurs. Vi slutter ikke før du ikke klarer å slutte – men i denne sammenhengen er det en god ting." + why_paragraph_2_italic: "jippi et merke" + why_paragraph_2_center: "men gøy som i" + why_paragraph_2_italic_caps: "NEI MAMMA JEG MÅ FULLFØRE DETTE BRETTET!" + why_paragraph_2_suffix: "Det er derfor CodeCombat er et flerspiller-spill, ikke et spillaktig programmeringskurs. Vi slutter ikke før du ikke klarer å slutte – men i denne sammenhengen er det en god ting." why_paragraph_3: "Hvis du skal bli avhengig av et spill, bli avhengig av dette og bli en av trollmennene i den nye teknologiske tidsalderen." press_title: "Bloggere/Presse" press_paragraph_1_prefix: "Har du lyst til å skrive om oss? Last gjerne ned og bruk alle ressursene i vår" - press_paragraph_1_link: "presse pakke" + press_paragraph_1_link: "pressepakke" press_paragraph_1_suffix: ". Alle logoer og bilder kan brukes uten å kontakte oss direkte." team: "Teamet" - george_title: "Adm.dir." #"CEO" - george_blurb: "Business-nisse" #"Businesser" - scott_title: "Programmerer" - scott_blurb: "Den fornuftige" #"Reasonable One" - nick_title: "Programmerer" + george_title: "Adm.dir." # {change} + george_blurb: "Business-nisse" + scott_title: "Programmerer" # {change} + scott_blurb: "Den fornuftige" + nick_title: "Programmerer" # {change} nick_blurb: "Motivasjonsguru" michael_title: "Programmerer" - michael_blurb: "System Administrator" - matt_title: "Programmerer" - matt_blurb: "Syklist" #Bicyclist" + michael_blurb: "Systemadministrator" + matt_title: "Programmerer" # {change} + matt_blurb: "Syklist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" -# versions: - save_version_title: "Lagre ny versjon" - new_major_version: "Ny hovedversjon" #"New Major Version" - cla_prefix: "For å lagre endringer må du først signere på vår" #"To save changes, first you must agree to our" -# cla_url: "CLA" -# cla_suffix: "." - cla_agree: "BEKREFT" #"I AGREE" + teachers: + title: "CodeCombat for lærere" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." + sys_requirements_title: "Systemkrav" + sys_requirements_1: "Siden CodeCombat er et spill, er det mer intensivt for datamaskiner å kjøre jevnt enn opplæringsvideo eller -tekst. Vi har optimert det for å kjøre raskt på alle moderne nettlesere, og på eldre maskiner slik at alle kan spille. Når det er sagt, her følger våre anbefalinger for å gå mest ut av din Hour of Code:" # {change} + sys_requirements_2: "Bruk nye versjoner av Chrome eller Firefox." # {change} + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." + + versions: + save_version_title: "Lagre ny version" + new_major_version: "Ny hovedversjon" + submitting_patch: "Sender inn patch..." + cla_prefix: "For å lagre endringer må du først akseptere vår" + cla_url: "CLA" + cla_suffix: "." + cla_agree: "JEG AKSEPTERER" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Kontakt CodeCombat" welcome: "Vi setter pris på å høre fra deg! Bruk dette skjemaet for å sende oss en epost." - contribute_prefix: "Hvis du er interessert i å bidra, sjekk ut vår " - contribute_page: "bidragsside" - contribute_suffix: "!" forum_prefix: "For allment tilgjengelige henvendelser, vennligst prøv " forum_page: "forumet vårt" forum_suffix: " i stedet." - send: "Send Tilbakemelding" + faq_prefix: "Det finnes også en" + faq: "OSS" + subscribe_prefix: "Hvis du trenger hjelp med å forstå en level, vennligst" + subscribe: "kjøp et abonnement på CodeCombat" + subscribe_suffix: "og vi hjelper deg gjerne med koden din." + subscriber_support: "Siden du er en CodeCombat-abonnent, vil din e-post få fortrinn hos kundestøtte." + screenshot_included: "Skjermbilde vedlagt." + where_reply: "Hvor skal vi sende svaret?" + send: "Send tilbakemelding" contact_candidate: "Kontakt kandidat" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -454,22 +694,29 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg autosave: "Endringer lagres automatisk" me_tab: "Meg" picture_tab: "Bilde" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" upload_picture: "Last opp bilde" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Passord" emails_tab: "Epost" admin: "Administrator" new_password: "Nytt Passord" new_password_verify: "Bekreft passord" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "Epost Abonnement" email_subscriptions_none: "Ingen Epost Abonnement." email_announcements: "Kunngjøringer" email_announcements_description: "Få epost om siste nytt og utvikling fra CodeCombat." email_notifications: "Varsler" email_notifications_summary: "Innstillinger for personlige, automatiske epostvarsler relatert til din CodeCombat aktivitet." - email_any_notes: "Alle Varsler" #"Any Notifications" - email_any_notes_description: "Skru av for å stoppe alle aktivitetsvarsler" #"Disable to stop all activity notification emails." + email_any_notes: "Alle Varsler" + email_any_notes_description: "Skru av for å stoppe alle aktivitetsvarsler" email_news: "Nyheter" - email_recruit_notes: "Jobbtilbud" #"Job Opportunities" + email_recruit_notes: "Jobbtilbud" email_recruit_notes_description: "Hvis du spiller veldig godt kontakter vi deg kanskje angående en (bedre) jobb." contributor_emails: "Epost for bidragsyter-klasser" contribute_prefix: "Vi leter etter folk som vil være med på moroa! Sjekk ut " @@ -480,66 +727,114 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg saved: "Endringer Lagret" password_mismatch: "Passordene er ikke like." password_repeat: "Vennligst gjenta passordet." -# job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated + job_profile: "Jobb Profil" # Rest of this section (the job profile stuff and wizard stuff) is deprecated # job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - wizard_tab: "Trollmann" - wizard_color: "Farge på Trollmannens Klær" + sample_profile: "Se en eksempelprofil" + view_profile: "Vis Profilen" keyboard_shortcuts: keyboard_shortcuts: "Hurtigtaster" space: "Mellomrom" enter: "Enter" +# press_enter: "press enter" escape: "Escape" shift: "Shift" - run_code: "Kjør koden." #"Run current code." + run_code: "Kjør koden." run_real_time: "Kjør i sanntid." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." + continue_script: "Fortsett forbi det kjørende skriptet." + skip_scripts: "Hopp over alle skript som kan hoppes over." toggle_playback: "Play/Pause." - scrub_playback: "Spol tiden frem og tilbake" #"Scrub back and forward through time." - single_scrub_playback: "Spol tiden frem og tilbake, steg for steg" #"Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." - toggle_debug: "Skru på/av feilrettingsvindu" #"Toggle debug display." - toggle_grid: "Skru på/av rutenett" #"Toggle grid overlay." - toggle_pathfinding: "Skru på/av stifinnervindu" #"Toggle pathfinding overlay." - beautify: "Gjør koden penere med standard formattering." #"Beautify your code by standardizing its formatting." + scrub_playback: "Spol tiden frem og tilbake" + single_scrub_playback: "Spol tiden frem og tilbake, steg for steg" + scrub_execution: "Spol gjennom gjeldende trylleformular-kjøring." + toggle_debug: "Skru på/av feilrettingsvindu" + toggle_grid: "Skru på/av rutenett" + toggle_pathfinding: "Skru på/av stifinnervindu" + beautify: "Gjør koden penere med standard formattering." maximize_editor: "Maksimer/minimer kode editor." - move_wizard: "Flytt trollmannen din rundt på brettet." community: main_title: "CodeCombat Fellesskapet" introduction: "Sjekk ut de forskjellige måtene du kan involvere deg på og velg det du synes er gøy. Vi ser frem til å jobbe sammen med deg!" - level_editor_prefix: "Bruk CodeCombat's" #"Use the CodeCombat" + level_editor_prefix: "Bruk CodeCombat's" level_editor_suffix: "til å lage og redigere brett. Brukere har laget sine egne brett for klassen sin, venner, elever og søsken. Hvis det høres overveldende ut å lage et nytt brett kan du begynne med å kopiere et av våre!" - thang_editor_prefix: "Vi kaller gjenstander i spillet for 'thang'. Bruk vår" #"We call units within the game 'thangs'. Use the" + thang_editor_prefix: "Vi kaller gjenstander i spillet for 'thang'. Bruk vår" thang_editor_suffix: "for å endre utseende og egenskapene til ting i spillet. La en figur kaste prosjektiler, endre retningen på en animasjon, endre helsen til en enhet eller last opp dine egne vektorfigurer." article_editor_prefix: "Funnet en feil i dokumentasjonen? Vil du skrive instruksjoner til noe du har laget selv? Sjekk ut vår" - article_editor_suffix: "og hjelp CodeCombat spillere til å få mest mulig ut av spillet." #"and help CodeCombat players get the most out of their playtime." - find_us: "Finn oss på disse tjenestene" #"Find us on these sites" - social_blog: "Les CodeCombat bloggen på Sett" #"Read the CodeCombat blog on Sett" - social_discource: "Diskuter CodeCombat i forumet vårt på Discourse" #"Join the discussion on our Discourse forum" + article_editor_suffix: "og hjelp CodeCombat spillere til å få mest mulig ut av spillet." + find_us: "Finn oss på disse tjenestene" + social_blog: "Les CodeCombat bloggen på Sett" + social_discource: "Diskuter CodeCombat i forumet vårt på Discourse" social_facebook: "Lik CodeCombat på Facebook" social_twitter: "Følg CodeCombat på Twitter" social_gplus: "Følg CodeCombat på Google+" social_hipchat: "Chat med oss i det åpne CodeCombat rommet på HipChat" contribute_to_the_project: "Bidra på prosjektet" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: - archmage_title: "Erketrollmann" #"Archmage" - archmage_title_description: "(Koder)" #"(Coder)" + archmage_title: "Erketrollmann" + archmage_title_description: "(Koder)" + archmage_summary: "Hvis du er en utvikler som er interessert i å lage utdanningsrettede spill, bli en erketrollmann, og hjelp oss bygge CodeCombat!" artisan_title: "Artisan" - artisan_title_description: "(Brettbygger)" #"(Level Builder)" - adventurer_title: "Eventyrer" #"Adventurer" - adventurer_title_description: "(Spilltester)" #"(Level Playtester)" - scribe_title: "Skriver" #"Scribe" - scribe_title_description: "(Artikkelforfatter)" #"(Article Editor)" + artisan_title_description: "(Brettbygger)" + artisan_summary: "Bygg og del brett som du og vennene dine kan spille. Bli en Artisan for å lære kunsten å lære andre å programmere." + adventurer_title: "Eventyrer" + adventurer_title_description: "(Spilltester)" + adventurer_summary: "Få våre nye brett (til-og-med innhold for abonnenter) gratis, en uke tidligere, og hjelp oss finne og fjerne feil før de blir gjort tilgjengelige for alle." + scribe_title: "Skriver" + scribe_title_description: "(Artikkelforfatter)" + scribe_summary: "God kode trenger god dokumentasjon. Skriv, rediger, og forbedre dok'en som leses av millioner av spillere, verden over." diplomat_title: "Diplomat" - diplomat_title_description: "(Oversetter)" #"(Translator)" + diplomat_title_description: "(Oversetter)" + diplomat_summary: "CodeCombat er oversatt til 45+ språk av våre diplomater. Hjelp oss ved å bidra med oversettelser." ambassador_title: "Ambassadør" - ambassador_title_description: "(Brukerstøtte)" #"(Support)" + ambassador_title_description: "(Brukerstøtte)" + ambassador_summary: "Temm våre forumbrukere og tilby hjelp for de med spørsmål. Våre ambassadører representerer CodeCombat ute i verden." editor: main_title: "CodeCombat Editorer" @@ -547,163 +842,168 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg thang_title: "Thang Editor" level_title: "Brett Editor" achievement_title: "Prestasjons Editor" +# poll_title: "Poll Editor" back: "Tilbake" revert: "Tilbakestill" revert_models: "Tilbakestill Modeller" - pick_a_terrain: "Velg Terreng" #"Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." + pick_a_terrain: "Velg Terreng" + dungeon: "Grotte" + indoor: "Innendørs" + desert: "Ørken" + grassy: "Gresset" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Liten" + large: "Stor" + fork_title: "Lag ny forgrening" + fork_creating: "Lager forgrening..." generate_terrain: "Generer Terreng" more: "Mer" wiki: "Wiki" # live_chat: "Live Chat" -# level_some_options: "Some Options?" + thang_main: "Hoved" +# thang_spritesheets: "Spritesheets" + thang_colors: "Farger" + level_some_options: "Noen valg?" level_tab_thangs: "Thangs" - level_tab_scripts: "Skript" #"Scripts" + level_tab_scripts: "Skript" level_tab_settings: "Innstillinger" level_tab_components: "Komponenter" level_tab_systems: "Systemer" level_tab_docs: "Dokumentasjon" -# level_tab_thangs_title: "Current Thangs" + level_tab_thangs_title: "Gjeldende Thanger" level_tab_thangs_all: "Alle" -# level_tab_thangs_conditions: "Starting Conditions" + level_tab_thangs_conditions: "Utgangsforhold" level_tab_thangs_add: "Legg til Thangs" +# level_tab_thangs_search: "Search thangs" + add_components: "Legg til komponenter" + component_configs: "Komponentoppsett" + config_thang: "Dobbeltklikk for å konfigurere en thang" delete: "Slett" - duplicate: "Kopier" #"Duplicate" + duplicate: "Kopier" + stop_duplicate: "Stopp duplikat" rotate: "Rotér" level_settings_title: "Innstillinger" -# level_component_tab_title: "Current Components" + level_component_tab_title: "Gjeldende Komponenter" level_component_btn_new: "Lag Ny Komponent" -# level_systems_tab_title: "Current Systems" + level_systems_tab_title: "Gjeldende Systemer" level_systems_btn_new: "Lag Nytt System" level_systems_btn_add: "Legg Til System" - level_components_title: "Tilbake til alle Thangs" #"Back to All Thangs" + level_components_title: "Tilbake til alle Thangs" level_components_type: "Type" level_component_edit_title: "Rediger Component" -# level_component_config_schema: "Config Schema" + level_component_config_schema: "Config-skjema" level_component_settings: "Innstillinger" level_system_edit_title: "Rediger System" create_system_title: "Lag Nytt System" new_component_title: "Lag Ny Komponent" new_component_field_system: "System" new_article_title: "Lag Ny Artikkel" - new_thang_title: "Lag en ny Thang Type" #"Create a New Thang Type" + new_thang_title: "Lag en ny Thang Type" new_level_title: "Lag et nytt Brett" new_article_title_login: "Logg inn for å lage en ny Artikkel" new_thang_title_login: "Log inn inn for å lage en ny Thang Type" new_level_title_login: "Logg inn for å lage et nytt Brett" new_achievement_title: "Lag en ny Prestasjon" new_achievement_title_login: "Logg inn for å lage en ny Prestasjon" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" article_search_title: "Søk i Artikler her" thang_search_title: "Søk i Thang Typer her" level_search_title: "Søk i Brett her" achievement_search_title: "Søk i Prestasjoner" - read_only_warning2: "Merk: Du kan ikke lagre endringene fordi du ikke er logget inn." #"Note: you can't save any edits here, because you're not logged in." - no_achievements: "Ingen prestasjoner er lagt til på dette brettet ennå." #"No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" -# pop_i18n: "Populate I18N" +# poll_search_title: "Search Polls" + read_only_warning2: "Merk: Du kan ikke lagre endringene fordi du ikke er logget inn." + no_achievements: "Ingen prestasjoner er lagt til på dette brettet ennå." + achievement_query_misc: "Oppnådde nøkkelmål av diverse" + achievement_query_goals: "Oppnådde nøkkelmål" + level_completion: "Komplett Nivå" + pop_i18n: "Fullfør I18N" + tasks: "Oppgaver" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Forhåndsvis" edit_article_title: "Rediger Artikkel" +# polls: +# priority: "Priority" + contribute: - page_title: "Bidrag" #"Contributing" - character_classes_title: "Karakterklasser" #"Character Classes" - introduction_desc_intro: "Vi har store forventninger til CodeCombat." - introduction_desc_pref: "Vi ønsker at det skal være stedet hvor programmerere av alle slag kommer for å lære og leke sammen, for å introdusere andre for programmeringens fabelaktige verden, og som gjenspeiler de beste sidene ved felleskapet. Vi hverken kan eller ønsker å gjøre det alene; det som gjør prosjekter som GitHub, Stack Overflow and Linux så bra er at folk som bruker dem også bygger videre på dem. Derfor er " - introduction_desc_github_url: "CodeCombat helt åpen kildekode" - introduction_desc_suf: ", og vi ønsker å gi deg så mange måter å delta på som mulig og gjøre prosjektet til like mye ditt som vårt." - introduction_desc_ending: "Vi håper du vil være med på moroa!" #"We hope you'll join our party!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy og Matt" - alert_account_message_intro: "Heisann!" #"Hey there!" + page_title: "Bidra" + intro_blurb: "CodeCombat er 100% åpen kildekode! Hundrevis av dedikerte spillere har hjulpet oss å bygge spillet til det det er blitt i dag. Foren deg med oss og bli med og skriv neste kapittel i CodeCombat sin søken etter å lære hele verden koding!" + alert_account_message_intro: "Heisann!" alert_account_message: "For å abonnere på klasse-eposter må du være logget inn først." - archmage_summary: "Interesert i å jobbe med spillgrafikk, brukergrensesnitt, database and server organisering, flerspiller nettverk, fysikk, lyd, eller spillmotor ytelse? Vil du hjelpe til å lage et spill som hjelper folk å lære det som du allerede er flink til? Vi hare mye å gjøre og hvis du er en erfaren programmerer som har lyst til å videreutvikle CodeCombat, da er dette klassen for deg. Vi vil gjerne ha din hjelp til å lage det beste programmeringsspillet noensinne." - archmage_introduction: "En av de beste tingene med å lage spill er at det bestaår av så mye forskjellig. Grafikk, lyd, sanntidsnettverk, sosiale nettverk, og selvfølgelig mange av de vanlige aspektene ved programmering, fra lav-nivå database drift og server administrasjon til design og bygging av brukergrensesnitt. Det er mye å gjøre og hvis du er en erfaren utvikler som har lyst til å dykke ned i de tekniske detaljene i CodeCombat, da er dette kanskje klassen for deg. Vi vil veldig gjerne ha din hjelp til å lage det beste programmeringsspillet noensinne." - class_attributes: "Klasseegenskaper" #Class Attributes" + archmage_introduction: "En av de beste tingene med å lage spill er at det består av så mye forskjellig. Grafikk, lyd, sanntidsnettverk, sosiale nettverk, og selvfølgelig mange av de vanlige aspektene ved programmering, fra lav-nivå database drift og serveradministrasjon til design og bygging av brukergrensesnitt. Det er mye å gjøre og hvis du er en erfaren utvikler som har lyst til å dykke ned i de tekniske detaljene i CodeCombat, da er dette kanskje klassen for deg. Vi vil veldig gjerne ha din hjelp til å lage det beste programmeringsspillet noensinne." + class_attributes: "Klasseegenskaper" archmage_attribute_1_pref: "Kunnskap om " archmage_attribute_1_suf: ", eller lyst til å lære. Mesteparten av koden vår er skrevet i dette språket. Hvis du liker Python eller Ruby vil du føle deg hjemme. Det er JavaScript, men med en hyggeligere syntaks." archmage_attribute_2: "Litt erfaring med programmering og masse personlig initativ. Vi hjelper deg til å få oversikten, men vi kan ikke bruke mye tid på å lære deg opp." - how_to_join: "Hvordan bli med" #"How To Join" + how_to_join: "Hvordan bli med" join_desc_1: "Alle kan hjelpe til! Ta en titt på siden vår på " join_desc_2: "for å komme i gang, og kryss av i boksen nedenfor for å merke deg selv som en modig Erketrollmann og få de siste nyhetene på epost. Vil du prate med oss om hva du kan gjøre og hvordan du kan involvere deg mer? " join_desc_3: ", eller finn oss i vårt " join_desc_4: "så tar vi det derfra!" join_url_email: "Send oss en epost" join_url_hipchat: "offentlige HipChat rom" - more_about_archmage: "Lær mer om å bli en Erketrollmann" archmage_subscribe_desc: "Få epost om nye muligheter til å kode og kunngjøringer." - artisan_summary_pref: "Vil du lage nye brett eller utvide arsenalet i CodeCombat? Folk spiller gjennom innholdet vår raskere enn vi kan lage det! Foreløpig er brett-editoren vår ganske enkel, så vær forberedt. Å lage nye brett vil være litt utfordrende og ustabilt. Hvis du har visjoner om kampanjer med alt fra for-løkker til" - artisan_summary_suf: ", da er denne klassen for deg." artisan_introduction_pref: "Vi må konstruere flere nye brett! Folk skriker etter mer innhold, og vi klarer bare å bygge så mange selv. Akkurat nå er arbeidsverktøyet ditt bare på nivå 1; brett-editoren vår er bare såvidt brukbar, selv for de som har laget den, så vær forberedt. Hvis du har visjoner om kampanjer med alt fra for-løkker til" artisan_introduction_suf: ", da er denne klassen kanskje for deg." artisan_attribute_1: "All tidligere erfaring med å lage lignende innhold er et pluss, som for eksempel Blizzard's brett-editor. Men det er ikke påkrevd!" artisan_attribute_2: "Lyst til å gjøre massevis av testing og repetisjoner. For å lage gode brett må man gi dem til andre og observere når de spiller dem, og være forberedt på å finne massevis av tings som må fikses." artisan_attribute_3: "Inntil videre bør man ha utholdenhet på nivå med en Eventyrer. Brett-editoren vår er i en veldig tidlig fase og kan være frustrerende å bruke. Du er herved advart!" artisan_join_desc: "Brett-editoren brukes omtrent på følgende måte" - artisan_join_step1: "Les dokumentasjonen." + artisan_join_step1: "Les dokumentasjonen." artisan_join_step2: "Lag et nytt brett og utforsk eksisterende brett." artisan_join_step3: "Finn oss i det offentlige HipChat rommet vårt for å få hjelp." artisan_join_step4: "Legg ut brettene dine på forumet for å få tilbakemeldinger." - more_about_artisan: "Lær mer om å bli en Artisan" artisan_subscribe_desc: "Få epost om oppdateringer i brett-editoren og kunngjøringer." - adventurer_summary: "La oss være tydelige på hva din rolle er: du må ta støyten. Du kommer til å få mye juling. Vi trenger folk som kan prøve helt nye brett og hjelpe oss å finne ut hvordan de kan gjøres bedre. Smerten vil bli enorm; å lage gode spill er en lang prosess og ingen får ting riktig første gangen. Hvis du kan holde ut og tåler en støyt, da er denne klassen for deg." adventurer_introduction: "La oss være tydelige på hva din rolle er: du må ta støyten. Du kommer til å få mye juling. Vi trenger folk som kan prøve helt nye brett og hjelpe oss å finne ut hvordan de kan gjøres bedre. Smerten vil bli enorm; å lage gode spill er en lang prosess og ingen får ting riktig første gangen. Hvis du kan holde ut og tåler en støyt, da er kanskje denne klassen for deg." adventurer_attribute_1: "Tørster etter kunnskap. Du vil lære å kode og vi vil gjerne lære deg å kode. Selv om det kanskje blir du som gjør mesteparten av bortlæringen i dette tilfellet." adventurer_attribute_2: "Karismatisk. Vær hyggelig men tydelig på hvor det trengs forbedringer, og kom med forslag til hvordan ting kan bli bedre." adventurer_join_pref: "Finn (eller rekrutter!) en Artisan og jobb sammen med dem, eller kryss av i boksen under for å motta epost når det er nye brett som må testes. Vi poster også om brett som trenger testing på nettverkene våre, som" - adventurer_forum_url: "forumet vårt" - adventurer_join_suf: ", så hvis du foretrekker å få varsler derfra i stedet kan du registrere deg der!" - more_about_adventurer: "Lær mer om å bli en Eventyrer" + adventurer_forum_url: "forumet vårt" + adventurer_join_suf: "så hvis du foretrekker å få varsler derfra i stedet kan du registrere deg der!" adventurer_subscribe_desc: "Få epost når det er nye brett som må testes." - scribe_summary_pref: "CodeCombat skal ikke bare være en samling av brett. Det skal også være en kilde til kunnskap om programmering som spillerene kan bruke. På den måten kan en Artisan linke til en detaljert artikkel som spilleren kan lære av, noe lignende det som " - scribe_summary_suf: " har bygget opp. Hvis du liker å forklare programmeringskonsepter, da er denne klassen for deg." - cribe_introduction_pref: "CodeCombat skal ikke bare være en samling av brett. Det skal også være en kilde til kunnskap, en wiki med programmeringskonsepter som kan brukes i brettene. Slik at i stedet for at hver Artisan må forklare i detalj hva en sammenligningsoperator er kan de bare linke brettet sitt til en eksisterende Artikkelen som forklarer konseptet. Noe lignende det som " + scribe_introduction_pref: "CodeCombat skal ikke bare være en samling med brett. Det skal også være en kilde til kunnskap, en wiki med programmeringskonsepter som kan brukes i brettene. Slik at i stedet for at hver Artisan må forklare i detalj hva en sammenligningsoperator er kan de bare linke brettet sitt til en eksisterende artikkelen som forklarer konseptet. Noe lignende det som " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " har bygget opp. Hvis du synes det gøy å forklare programmeringskonsepter i Markdown format, da er denne klassen kanskje for deg." scribe_attribute_1: "Å være flink med ord er egentlig det eneste som trengs. Ikke bare grammatikk og rettskriving, men også evne til å formidle kompliserte konsepter til andre." contact_us_url: "Kontakt oss" scribe_join_description: "fortell oss litt om deg selv, din erfaring med programmering og hva slags ting du har lyst til å skrive om. Så tar vi det derfra!" - more_about_scribe: "Ler mer om å bli en Skriver" scribe_subscribe_desc: "Få epost om kunngjøringer relatert til artikkelskriving." - diplomat_summary: "Det er stor interesse for CodeCombat i land der de ikke snakker engelsk! Vi er på jakt etter oversettere som er villig til å bruke tid på å oversette all teksten i spillet og på nettsidene slik at CodeCombat er tilgjengelig over hele verden så snart som mulig. Hvis du vil hjelpe til å gjøre CodeCombat internasjonalt, da er denne klassen for deg." diplomat_introduction_pref: "Hvis det er en ting vi lærte av " - diplomat_launch_url: "lanseringen i Oktober" #"launch in October" + diplomat_launch_url: "lanseringen i oktober" diplomat_introduction_suf: "så er det at det er stor interesse for CodeCombat i andre land! Vi bygger et korps av oversettere som er ivrige etter å gjøre om ett sett av ord til et annet sett av ord, slik at CodeCombat blir tilgengelig i så store deler av verden som mulig. Hvis du liker å få sniktitte på kommende innhold og å bringe disse brettene til dine lansdmenn fortere enn svint, da er denne klassen kanskje for deg." diplomat_attribute_1: "Gode kunnskaper i engelsk og det språket du vil oversette til. Når man skal formidle kompliserte konsepter er det viktig å mestre begge språkene godt!" - diplomat_i18n_page_prefix: "Du kan beynne å oversette brett ved å gå til vår" #"You can start translating our levels by going to our" - diplomat_i18n_page: "oversettingsside" #"translations page" + diplomat_i18n_page_prefix: "Du kan beynne å oversette brett ved å gå til vår" + diplomat_i18n_page: "oversettingsside" diplomat_i18n_page_suffix: ", eller oversette grensesnittet og websidene på GitHub." - diplomat_join_pref_github: "Finn din 'locale' fil " #"Find your language locale file " + diplomat_join_pref_github: "Finn din 'locale' fil " diplomat_github_url: "på GitHub" diplomat_join_suf_github: ", rediger den online, og send oss en 'pull request'. Og kryss av i boksen under for å motta oppdateringer relatert til internasjonalisering!" - more_about_diplomat: "Lær mer om å bli en Diplomat" diplomat_subscribe_desc: "Få epost om i18n oppdateringer og nye brett som må oversettes." - ambassador_summary: "Vi prøver å bygge et fellesskap, og alle felleskap trenger støttespillere når det oppstår problemer. Vi har chat, epost og sosiale nettverk som spillerne kan bruke til å bli bedre kjent med spillet. Hvis du har lyst til å hjelpe folk til å involvere seg mer, ha det gøy, og lære litt programmering, da er denne klassen for deg." - ambassador_introduction: "Det er et felleskap vi prøver å bygge her, og dere er bindeleddene. Vi har Olark chat, epost, og sosiale nettverkmed mange mennesker så snakke med og hjelpe med å bli bedre kjent med spillet. Hvis du vil hjelpe folk å bli mer involvert, ha det gøy, og får en god følelse av stemningen i CodeCombat og hva vi prøver å få til, da er denne klassen kanskje for deg.." + ambassador_introduction: "Det er et felleskap vi prøver å bygge her, og dere er bindeleddene. Vi har forum, chat, epost, og sosiale nettverk med mange mennesker å snakke med, og hjelpe til å bli bedre kjent med spillet, og lære fra. Hvis du vil hjelpe folk å bli mer involvert og ha det gøy, og får en god følelse av stemningen i CodeCombat og hva vi prøver å få til, da er denne klassen kanskje for deg.." ambassador_attribute_1: "Flink til å kommunisere. Flink til å identifisere problemene spillere har og hjelpe dem med å løse dem. Og i tillegg holde resten av oss informert om hva spillerne sier, hva de liker og ikke liker, og hva de vil ha mer av!" ambassador_join_desc: "fortell oss litt om deg selv, hva du har drevet med tidligere og hva du er interessert i å gjøre. Så tar vi det derfra!" - ambassador_join_note_strong: "Merk" #"Note" + ambassador_join_note_strong: "Merk" ambassador_join_note_desc: "En ting vi prioriterer høyt er å utvikle funksjonalitet i flerspillerdelen som lar spillere som har vanskeligheter med oppgaven tilkalle erfarne trollmenn som kan hjelpe dem. Dette vil være en flott måte for Ambasadører å hjelpe til på. Vi holder dere oppdatert!" - more_about_ambassador: "Lær mer om å bli en Ambassadør" ambassador_subscribe_desc: "Få epost om oppdateringer relatert til brukerstøtte." changes_auto_save: "Endringene lagres automatisk når klikker på avkryssingsboksene." - diligent_scribes: "Våre flittige Skrivere:" #"Our Diligent Scribes:" - powerful_archmages: "Våre mektige Erketrollmenn:" #"Our Powerful Archmages:" - creative_artisans: "Våre kreative Artisaner:" #"Our Creative Artisans:" - brave_adventurers: "Våre tapre Eventyrere:" #"Our Brave Adventurers:" - translating_diplomats: "Våre oversettende Diplomater:" #"Our Translating Diplomats:" - helpful_ambassadors: "Våre hjelpsomme Ambassadører:" #Our Helpful Ambassadors:" + diligent_scribes: "Våre flittige Skrivere:" + powerful_archmages: "Våre mektige Erketrollmenn:" + creative_artisans: "Våre kreative Artisaner:" + brave_adventurers: "Våre tapre Eventyrere:" + translating_diplomats: "Våre oversettende Diplomater:" + helpful_ambassadors: "Våre hjelpsomme Ambassadører:" ladder: -# please_login: "Please log in first before playing a ladder game." + please_login: "Vennligst logg inn før du spiller stigespillet." my_matches: "Mine kamper" simulate: "Simuler" - simulation_explanation: "Ved å simulere spill kan du få ditt spill rangert raskere!" #"By simulating games you can get your game ranked faster!" + simulation_explanation: "Ved å simulere spill kan du få ditt spill rangert raskere!" simulate_games: "Simuler Spill!" simulate_all: "TILBAKESTILL OG SIMULER SPILL" games_simulated_by: "Spill simulert av deg:" @@ -711,8 +1011,8 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg games_simulated: "Spill simulert" games_played: "Spill spilt" ratio: "Forhold" - leaderboard: "Poengtavle" #"Leaderboard" - battle_as: "Kjemp som " #"Battle as " + leaderboard: "Poengtavle" + battle_as: "Kjemp som " summary_your: "Ditt " summary_matches: "Kamper - " summary_wins: " Seire, " @@ -729,137 +1029,197 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg no_ranked_matches_pre: "Ingen nye rangerte kamper for " no_ranked_matches_post: " laget! Spill mot noen motstandere og kom tilbake hit etterpå for å få spillet ditt rangert." choose_opponent: "Velg en motstander" - select_your_language: "Velg språk!" #"Select your language!" -# tutorial_play: "Play Tutorial" - tutorial_recommended: "Anbefalt hvis du ikke har spilt før" #"Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" - tutorial_not_sure: "Usikker på hva som foregår?" #"Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." + select_your_language: "Velg språk!" + tutorial_play: "Spill opplæringsspill" + tutorial_recommended: "Anbefalt hvis du ikke har spilt før" + tutorial_skip: "Hopp over opplæring" + tutorial_not_sure: "Usikker på hva som foregår?" + tutorial_play_first: "Spill opplæringsspillet først." simple_ai: "Enkel AI" warmup: "Oppvarming" - friends_playing: "Venner som spiller" #"Friends Playing" - log_in_for_friends: "Logg inn for å spille med vennene dine" #"Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" - invite_friends_to_battle: "Inviter vennene dine til å møte deg i strid!" #"Invite your friends to join you in battle!" -# fight: "Fight!" - watch_victory: "Se seieren" #"Watch your victory" - defeat_the: "Overvinn" #"Defeat the" - tournament_ends: "Turneringen slutter" #"Tournament ends" - tournament_ended: "Turneringen sluttet " #"Tournament ended" - tournament_rules: "Turneringsregler" #"Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" + friends_playing: "Venner som spiller" + log_in_for_friends: "Logg inn for å spille med vennene dine" + social_connect_blurb: "Koble til og spill mot vennene dine!" + invite_friends_to_battle: "Inviter vennene dine til å møte deg i strid!" + fight: "Slåss!" + watch_victory: "Se seieren" + defeat_the: "Overvinn" +# tournament_started: ", started" + tournament_ends: "Turneringen slutter" + tournament_ended: "Turneringen sluttet " + tournament_rules: "Turneringsregler" + tournament_blurb: "Skriv kode, samle gull, bygg hærer, knus fiender, vinn premier, og oppgrader karrieren din i vår $40,000 Greed turnering! Sjekk detaljene på" + tournament_blurb_criss_cross: "Vinn bud, bygg stier, overlist motstandere, ta juveler, og oppgrader karrieren din i vår Criss-Cross turnering! Sjekk detaljene på" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" + tournament_blurb_blog: "bloggen vår" rules: "Regler" winners: "Vinnere" user: -# stats: "Stats" - singleplayer_title: "Enspiller Brett" #"Singleplayer Levels" - multiplayer_title: "Flerspiller Brett" #"Multiplayer Levels" + stats: "Statistikk" + singleplayer_title: "Enspillerbrett" + multiplayer_title: "Flerspillerbrett" achievements_title: "Prestasjoner" - last_played: "Sist Spilt" -# status: "Status" + last_played: "Sist spilt" + status: "Status" status_completed: "Fullført" status_unfinished: "Uferdig" - no_singleplayer: "Du har ikke spilt noen enspiller brett ennå." #"No Singleplayer games played yet." - no_multiplayer: "Du har ikke spilt noen flerspiller brett ennå." #"No Multiplayer games played yet." - no_achievements: "Ingen prestasjoner oppnådd ennå." #"No Achievements earned yet." - favorite_prefix: "Favorittspråket er " #"Favorite language is " -# favorite_postfix: "." + no_singleplayer: "Du har ikke spilt noen enspillerbrett ennå." + no_multiplayer: "Du har ikke spilt noen flerspillerbrett ennå." + no_achievements: "Ingen prestasjoner oppnådd ennå." + favorite_prefix: "Favorittspråket er " + favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." achievements: - last_earned: "Sist oppnådd" #"Last Earned" -# amount_achieved: "Amount" + last_earned: "Sist oppnådd" + amount_achieved: "Mengde" achievement: "Prestasjon" - category_contributor: "Bidragsyter" #"Contributor" -# category_ladder: "Ladder" -# category_level: "Level" #FIXME: Brett eller nivå? + category_contributor: "Bidragsyter" + category_ladder: "Stige" + category_level: "Brett" category_miscellaneous: "Forskjellig" -# category_levels: "Levels" #FIXME: Brett eller nivå? + category_levels: "Brett" category_undefined: "Ukategorisert" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + current_xp_prefix: "" + current_xp_postfix: " totalt" + new_xp_prefix: "" + new_xp_postfix: " opptjent" + left_xp_prefix: "" + left_xp_infix: " til nivå " + left_xp_postfix: "" account: - recently_played: "Nylig Spilt" #"Recently Played" - no_recent_games: "Ingen spill spilt de siste to ukene." #"No games played during the past two weeks." + recently_played: "Nylig Spilt" + no_recent_games: "Ingen spill spilt de siste to ukene." + payments: "Betalinger" + purchased: "Kjøpt" + subscription: "Abonnement" +# invoices: "Invoices" + service_apple: "Apple" + service_web: "Web" + paid_on: "Betalt den" + service: "Tjeneste" + price: "Pris" + gems: "Juveler" + active: "Aktiv" + subscribed: "Innmeldt" + unsubscribed: "Utmeldt" + active_until: "Aktiv til" + cost: "Kostnad" + next_payment: "Neste Betaling" + card: "Kort" + status_unsubscribed_active: "Du har ikke et aktivt abonnement, og vil ikke bli fakturert, men kontoen din er aktiv enn så lenge." + status_unsubscribed: "Få tilgang til nye nivåer, helter, gjenstander, og bonus gems med et CodeCombat abonnement!" -# loading_error: -# could_not_load: "Error loading from server" - connection_failure: "Tilkobling feilet." #"Connection failed." - unauthorized: "Du må være pålogget. Har du informasjonskapsler (cookies) skrudd på?" #"You need to be signed in. Do you have cookies disabled?" - forbidden: "Du har ikke tilgang" #"You do not have the permissions." +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" + + loading_error: + could_not_load: "Feil ved lasting fra server" + connection_failure: "Tilkobling feilet." + unauthorized: "Du må være pålogget. Har du informasjonskapsler (cookies) skrudd på?" + forbidden: "Du har ikke tilgang" not_found: "Ikke funnet." - not_allowed: "Metoden er ikke tillat" #"Method not allowed." - timeout: "Tidsavbrudd på server" #"Server timeout." + not_allowed: "Metoden er ikke tillat" + timeout: "Tidsavbrudd på server" conflict: "Ressurskonflikt." - bad_input: "Feil i inndata" #"Bad input." - server_error: "Server feil." + bad_input: "Feil i inndata" + server_error: "Serverfeil." unknown: "Ukjent feil." -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" -# social_network_apis: "Social Network APIs" -# facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" -# facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" -# gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" -# user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" -# patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" -# thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" -# feedback: "Feedback" + resources: + sessions: "Sesjoner" + your_sessions: "Dine sesjoner" + level: "Nivå" + social_network_apis: "Sosiale Nettverk-APIer" + facebook_status: "Facebook status" + facebook_friends: "Facebook venner" + facebook_friend_sessions: "Facebook venners sesjoner" + gplus_friends: "G+ venner" + gplus_friend_sessions: "G+ venners sesjoner" + leaderboard: "Poengtavle" + user_schema: "Brukerskjema" + user_profile: "Brukerprofil" + patch: "Patch" + patches: "Patcher" + patched_model: "Kildedokument" + model: "Modell" + system: "System" + systems: "Systemer" + component: "Komponent" + components: "Komponenter" + thang: "Thang" + thangs: "Thangs" + level_session: "Din sesjon" + opponent_session: "Motstanders sesjon" + article: "Artikkel" + user_names: "Brukernavn" + thang_names: "Thang Navn" + files: "Filer" + top_simulators: "Topp Simulatorer" + source_document: "Kildedokument" + document: "Dokument" + sprite_sheet: "Sprite-ark" + employers: "Arbeidsgivere" + candidates: "Kandidater" + candidate_sessions: "Kandidat Sesjoner" + user_remark: "Brukerkommentar" + user_remarks: "Brukerkommentarer" + versions: "Versjoner" + items: "Gjenstander" +# hero: "Hero" + heroes: "Helter" + achievement: "Prestasjon" + clas: "CLAer" + play_counts: "Spillteller" + feedback: "Tilbakemelding" + payment_info: "Betalingsinformasjon" + campaigns: "Kampanjer" +# poll: "Poll" +# user_polls_record: "Poll Voting History" -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" -# guide: -# temp: "Temp" + delta: + added: "Opprettet" + modified: "Endret" +# not_modified: "Not Modified" + deleted: "Slettet" + moved_index: "Endret Index" + text_diff: "Tekst Diff" + merge_conflict_with: "MERGE KONFLIKT MED" + no_changes: "Ingen Endringer" + + guide: + temp: "Midlertidig" multiplayer: multiplayer_title: "Flerspillerinnstillinger" # We'll be changing this around significantly soon. Until then, it's not important to translate. @@ -874,61 +1234,55 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg legal: page_title: "Juridisk" opensource_intro: "CodeCombat er gratis å spille og helt åpen kildekode." - opensource_description_prefix: "Ta en titt på siden vår på " #"Check out " - github_url: "GitHub" #"our GitHub" + opensource_description_prefix: "Ta en titt på siden vår på " + github_url: "GitHub" opensource_description_center: "og hjelp gjerne til hvis du har lyst! CodeCombat er basert på dusinvis av åpen kildekode-prosjekter og vi er veldig glade i dem. Se " - archmage_wiki_url: "Erketrollmann-wikien" #"our Archmage wiki" + archmage_wiki_url: "Erketrollmann-wikien" opensource_description_suffix: "for en liste over programvare som gjør dette spillet mulig." - practices_title: "Respektfulle \"Best Practices\"" #"Respectful Best Practices" + practices_title: "Respektfulle \"Best Practices\"" practices_description: "Dette er våre løfter til deg som spiller, på litt mindre juridisk språk." - privacy_title: "Personvern" #"Privacy" - privacy_description: "Vi kommer ikke til å selge noen av dine personlige opplysninger. Vi planlegger å tjene penger på rekruttering en dag, men du kan føle deg helt trygg på at vi ikke vil gi ut dine personlige opplysninger til interesserte arbeidsgivere uten eksplisitt godkjenning fra deg." - security_title: "Sikkerhet" #"Security" + privacy_title: "Personvern" + privacy_description: "Vi vil ikke selge noe av din personlige informasjon." + security_title: "Sikkerhet" security_description: "Vi gjør alt vi kan for å beskytte dine personlige opplysninger. Som et åpen kildekode-prosjekt kan hvem som helst inspisere og forbedre sikkerhetssystemene våre." email_title: "Epost" email_description_prefix: "Vi kommer ikke til å oversvømme deg med søppelpost. Via " email_settings_url: "epost-innstillingene dine" email_description_suffix: "eller via linker i epostene vi sender til deg, kan du endre hvilke eposter du ønsker å motta og enkelt avslutte abonnementet når som helst." - cost_title: "Pris" #"Cost" + cost_title: "Pris" cost_description: "For øyeblikket er CodeCombat 100% gratis! Et av hovedmålene våre er å fortsette med det, sånn at flest mulig kan spille, uavhengig av livssituasjon. Hvis ting går dårlig må vi kanskje begynne å ta betalt for abonnement eller annet innhold, men vi vil helst slippe. Med litt flaks vil vi klare å holde selskapet i live med:" - recruitment_title: "Rekruttering" #"Recruitment" - recruitment_description_prefix: "Her på CodeCombat kommer du til å bli en mektig trollmann – ikke bare i spillet, men også i virkeligheten." - url_hire_programmers: "Arbeidsgivere klarer ikke å ansette programmerere raskt nok" #"No one can hire programmers fast enough" - recruitment_description_suffix: "så når du har skjerpet kunnskapene dine, og hvis du godkjenner det, kommer vi til å vise frem de beste kodeprestasjonene dine til de tusenvis av arbeidsgivere som sikler over sjansen til å ansette deg. De betaler oss litt, de betaler deg" - recruitment_description_italic: "mye" - recruitment_description_ending: "CodeCombat forblir gratis og alle er fornøyd. Det er planen." - copyrights_title: "Kopirettigheter og lisenser" #"Copyrights and Licenses" - contributor_title: "Lisensavtale for bidragsytere" #"Contributor License Agreement" + copyrights_title: "Kopirettigheter og lisenser" + contributor_title: "Lisensavtale for bidragsytere" contributor_description_prefix: "Alle bidrag, både gjort gjennom brukergrensesnittet og i kodelageret på GitHub, er underlagt vår" - cla_url: "Lisensavtale for Bidragsytere" #"CLA" #FIXME: Comma is inserted after this in the template - contributor_description_suffix: "(Contributor License Agreement - CLA), som du må bekrefte før du kan bidra på prosjektet." #"to which you should agree before contributing." - code_title: "Kode - MIT lisens" #"Code - MIT" + cla_url: "Lisensavtale for Bidragsytere" + contributor_description_suffix: "(Contributor License Agreement - CLA), som du må bekrefte før du kan bidra på prosjektet." + code_title: "Kode - MIT lisens" code_description_prefix: "All kildekode som er eid av CodeCombat eller ligger på codecombat.com, både i kodelageret på GitHub eller i codecombat.com databasen, er lisensiert med " mit_license_url: "MIT lisensen" code_description_suffix: "Dette inkluderer alle kode i Systemer og Komponenter som er gjort tilgjengelig av CodeCombat til bruk når man lager Brett." - art_title: "Grafikk og Musikk - Creative Commons" #"Art/Music - Creative Commons " - art_description_prefix: "Alt felles innhold er tilgjengelig under" #"All common content is available under the" + art_title: "Grafikk og Musikk - Creative Commons" + art_description_prefix: "Alt felles innhold er tilgjengelig under" cc_license_url: "Creative Commons Attribution 4.0 International License" art_description_suffix: "Felles innhold er alt innhold som er gjort tilgjengelig av CodeCombat til bruk når man lager Brett. Dette inkluderer:" art_music: "Musikk" art_sound: "Lyd" - art_artwork: "Illustrasjoner" #"Artwork" - art_sprites: "Figurer (Sprites)" #"Sprites" + art_artwork: "Illustrasjoner" + art_sprites: "Figurer (Sprites)" art_other: "Alle andre kreative verk, med unntak av kode, som er tilgjengelig når man lager Brett." art_access: "For øyeblikket finnes det ingen enkel og universell måte å få tak i disse ressursene. De kan enten lastes ned via nettaddressene som klienten bruker, vi kan hjelpe deg hvis du kontakter oss, eller du kan hjelpe oss å utvide systemet slik at disse ressursene blir lettere tilgjengelig." art_paragraph_1: "Ved navngiving oppgi codecombat.com og link til oss i nærheten av stedet hvor ressursen er brukt eller på et sted som egner seg for mediet. For eksempel:" use_list_1: "Hvis brukt i en film eller et annet spill, inkluder codecombat.com i rulleteksten." use_list_2: "Hvis brukt på en nettside, inkluder en link i nærheten av bruksstedet, for eksempel under et bilde, eller på en egen side hvor du kansje også navngir andre Creative Commons kilder og åpen kildekode-prosjekter som blir brukt på siden. Noe som allerede tydelig refererer til CodeCombat, som f.eks. i et blogginnlegg om CodeCombat, trenger ingen egen navngiving." art_paragraph_2: "Hvis innholdet som blir brukt ikke er laget av CodeCombat, men av en bruker på codecombat.com, navngi dem i stedet og følg navngivings anvisningene gitt i ressursens beskrivelse hvis tilgjengelig." - rights_title: "Forbeholdte rettigheter" #"Rights Reserved" + rights_title: "Forbeholdte rettigheter" rights_desc: "Alle rettigheter er forbeholdt for selve Brettene. Dette inkluderere: " - rights_scripts: "Skript" #"Scripts" - rights_unit: "Oppsett av enheter" #"Unit configuration" - rights_description: "Beskrivelser" #"Description" - rights_writings: "Tekster" #"Writings" + rights_scripts: "Skript" + rights_unit: "Oppsett av enheter" + rights_description: "Beskrivelser" + rights_writings: "Tekster" rights_media: "Media (lyder, musikk) og alt annet kreativt innhold som er laget spesielt for Brettet og ikke gjort allment tilgjengelig til bruk når man lager Brett." rights_clarification: "Altså: alt som er gjort tilgjengelig i Brett-editoren med det formål å brukes i nye Brett er under Creative Commons, mens det innholdet som blir laget med Brett-editoren eller som blir lastet opp under utviklingen av nye Brett er ikke." - nutshell_title: "I et nøtteskall" #"In a Nutshell" + nutshell_title: "I et nøtteskall" nutshell_description: "Alle ressursene vi har gjort tilgjengelig i Brett-editoren kan fritt brukes til å lage nye Brett. Men vi forbeholder oss rettighetene til å distribuere selve Brettene (som er laget på codecombat.com), slik at vi har muligheten til å kreve betaling for dem i fremtiden, dersom vi ender opp med å gjøre det." canonical: "Den engelske utgaven av dette dokumentet er den definitive og gjeldende utgaven. Hvis det er forskjeller i oversettelsen så er det den engelske utgaven som gjelder." @@ -952,22 +1306,6 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg # license: "license" # oreilly: "ebook of your choice" - wizard_settings: -# title: "Wizard Settings" - customize_avatar: "Tilpass figuren" #"Customize Your Avatar" - active: "Aktiv" - color: "Farge" - group: "Gruppe" - clothes: "Klær" - trim: "Detaljer" - cloud: "Sky" - team: "Lag" - spell: "Magi" - boots: "Støvler" - hue: "Fargetone" - saturation: "Metning" - lightness: "Lyshet" #"Lightness" #FIXME? - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" @@ -1119,22 +1457,22 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg # other_developers: "Other Developers" # inactive_developers: "Inactive Developers" -# admin: -# av_espionage: "Espionage" # Really not important to translate /admin controls. -# av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" -# av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" -# av_title: "Admin Views" -# av_entities_sub_title: "Entities" -# av_entities_users_url: "Users" -# av_entities_active_instances_url: "Active Instances" -# av_entities_employer_list_url: "Employer List" -# av_entities_candidates_list_url: "Candidate List" -# av_entities_user_code_problems_list_url: "User Code Problems List" -# av_other_sub_title: "Other" -# av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "User List" -# ucp_title: "User Code Problems" -# lg_title: "Latest Games" -# clas: "CLAs" + admin: + av_espionage: "Spionasje" # Really not important to translate /admin controls. + av_espionage_placeholder: "Epost eller brukernavn" + av_usersearch: "Brukersøk" + av_usersearch_placeholder: "Epost brukernavn, navn eller hva som helst" + av_usersearch_search: "Søk" + av_title: "Admin Visninger" + av_entities_sub_title: "Entiteter" + av_entities_users_url: "Brukere" + av_entities_active_instances_url: "Aktive Instanser" + av_entities_employer_list_url: "Arbeidsgiverliste" + av_entities_candidates_list_url: "Kandidatliste" + av_entities_user_code_problems_list_url: "Brukerkode-problem Liste" + av_other_sub_title: "Andre" + av_other_debug_base_url: "Base (for debugging base.jade)" + u_title: "Brukerliste" + ucp_title: "Brukerkode-problemer" + lg_title: "Siste Spill" + clas: "CLAer" diff --git a/app/locale/nl-BE.coffee b/app/locale/nl-BE.coffee index 8663012cb..bd13a4ff4 100644 --- a/app/locale/nl-BE.coffee +++ b/app/locale/nl-BE.coffee @@ -3,14 +3,15 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: slogan: "Leer programmeren door het spelen van een spel" no_ie: "CodeCombat werkt niet in IE8 of ouder. Sorry!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat is niet gemaakt voor mobiele apparaten en werkt misschien niet!" # Warning that shows up on mobile devices - play: "Speel" # The big play button that just starts playing a level + play: "Speel" # The big play button that opens up the campaign view. old_browser: "Uh oh, jouw browser is te oud om CodeCombat te kunnen spelen, Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Je kan toch proberen, maar het zal waarschijnlijk niet werken!" + ipad_browser: "Slecht nieuws: CodeCombat werkt niet op een iPad in de browser. Goed nieuws: onze eigen iPad-app wacht op goedkeuring van Apple." campaign: "Campagne" for_beginners: "Voor Beginners" multiplayer: "Multiplayer" # Not currently shown on home page for_developers: "Voor ontwikkelaars" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Of download voor iPad" nav: play: "Levels" # The top nav bar entry where players choose which levels to play @@ -19,7 +20,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: blog: "Blog" forum: "Forum" # account: "Account" -# profile: "Profile" + profile: "Profiel" # stats: "Stats" # code: "Code" admin: "Administrator" # Only shows up when you are an admin @@ -29,7 +30,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: about: "Over Ons" contact: "Contact" twitter_follow: "Volgen" -# teachers: "Teachers" + teachers: "Leraren" modal: close: "Sluiten" @@ -49,84 +50,84 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: play: play_as: "Speel als " # Ladder page spectate: "Toeschouwen" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play + players: "Spelers" # Hover over a level on /play + hours_played: "Gespeelde uren" # Hover over a level on /play # items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero + unlock: "Ontsluit" # For purchasing items and heroes + confirm: "Bevestig" + owned: "In bezit" # For items you own + locked: "Gesloten" + purchasable: "Te koop" # For a hero you unlocked but haven't purchased + available: "Beschikbaar" + skills_granted: "Vaardigheden toegestaan" # Property documentation details + heroes: "Helden" # Tooltip on hero shop button from /play + achievements: "Prestaties" # Tooltip on achievement list button from /play + account: "Account" # Tooltip on account button from /play + settings: "Instellingen" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "Volgende" # Go from choose hero to choose inventory before playing a level + change_hero: "Held wisselen" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + buy_gems: "Koop juwelen" +# subscription_required: "Subscription Required" + anonymous: "Anonieme speler" level_difficulty: "Moeilijkheidsgraad: " campaign_beginner: "Beginnercampagne" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Kies Je Level" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Je kunt meteen naar een van de levels hieronder springen, of de levels bespreken op " - adventurer_forum: "het Avonturiersforum" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... waarin je de toverkunst van het programmeren leert." - campaign_dev: "Willekeurige moeilijkere levels" - campaign_dev_description: "... waarin je de interface leert kennen terwijl je wat moeilijkers doet." +# adjust_volume: "Adjust volume" campaign_multiplayer: "Multiplayer Arena's" campaign_multiplayer_description: "... waarin je direct tegen andere spelers speelt." - campaign_player_created: "Door-spelers-gemaakt" - campaign_player_created_description: "... waarin je ten strijde trekt tegen de creativiteit van andere <a href=\"/contribute#artisan\">Ambachtelijke Tovenaars</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "Account maken" log_in: "Inloggen" logging_in: "Bezig met inloggen" log_out: "Uitloggen" - recover: "account herstellen" + forgot_password: "Paswoord vergeten?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" + signup_switch: "Account aanmaken?" signup: - create_account_title: "Maak een account aan om je vooruitgang op te slaan" - description: "Het is gratis. We hebben maar een paar dingen nodig en dan kan je aan de slag:" email_announcements: "Ontvang aankondigingen via email" - coppa: "13+ of niet uit de VS" - coppa_why: "(Waarom?)" creating: "Account aanmaken..." sign_up: "Aanmelden" log_in: "inloggen met wachtwoord" social_signup: "Of je kunt je registreren met Facebook of G+:" -# required: "You need to log in before you can go that way." + required: "Je moet eerst inloggen om die richting te kunnen volgen." + login_switch: "Heb je al een account?" recover: recover_account_title: "Herstel Account" send_password: "Verzend nieuw wachtwoord" # recovery_sent: "Recovery email sent." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" + items: + primary: "Eerste" + secondary: "Tweede" + armor: "Pantser" + accessories: "Bijhorigheden" # misc: "Misc" -# books: "Books" + books: "Boeken" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Bezig met laden..." saving: "Opslaan..." sending: "Verzenden..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: fork: "Fork" play: "Spelen" # When used as an action verb, like "Play next level" retry: "Probeer opnieuw" -# watch: "Watch" -# unwatch: "Unwatch" +# actions: "Actions" +# info: "Info" +# help: "Help" + watch: "Kijk" + unwatch: "Niet kijken" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" general: and: "en" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # date: "Date" body: "Inhoud" version: "Versie" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" commit_msg: "Commit Bericht" +# review: "Review" version_history: "Versie geschiedenis" version_history_for: "Versie geschiedenis voor: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" result: "Resultaat" results: "Resultaten" description: "Beschrijving" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: hard: "Moeilijk" player: "Speler" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" units: second: "seconde" @@ -182,56 +204,56 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: minutes: "minuten" hour: "uur" hours: "uren" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + day: "dag" + days: "dageb" + week: "week" + weeks: "weken" + month: "maand" + months: "maanden" + year: "jaar" + years: "jaren" play_level: done: "Klaar" home: "Home" # Not used any more, will be removed soon. # level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" + skip: "Sla over" # game_menu: "Game Menu" guide: "Handleiding" restart: "Herstarten" goals: "Doelen" -# goal: "Goal" + goal: "Doel" # running: "Running..." # success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" + incomplete: "Onvolledig" + timed_out: "De tijd is om" # failing: "Failing" action_timeline: "Actie tijdlijn" click_to_select: "Klik op een eenheid om deze te selecteren." -# control_bar_multiplayer: "Multiplayer" + control_bar_multiplayer: "Multiplayer" # control_bar_join_game: "Join Game" -# reload: "Reload" + reload: "Herlaad" reload_title: "Alle Code Herladen?" reload_really: "Weet je zeker dat je dit level tot het begin wilt herladen?" reload_confirm: "Herlaad Alles" + victory: "Overwinning" victory_title_prefix: "" victory_title_suffix: " Compleet" victory_sign_up: "Schrijf je in om je vooruitgang op te slaan" victory_sign_up_poke: "Wil je jouw code opslaan? Maak een gratis account aan!" victory_rate_the_level: "Beoordeel het level: " # Only in old-style levels. victory_return_to_ladder: "Keer terug naar de ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Speel Volgend Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "Ga verder" + victory_saving_progress: "Vooruitgang bewaren" victory_go_home: "Ga naar Home" # Only in old-style levels. victory_review: "Vertel ons meer!" # Only in old-style levels. victory_hour_of_code_done: "Ben Je Klaar?" victory_hour_of_code_done_yes: "Ja, ik ben klaar met mijn Hour of Code!" + victory_experience_gained: "XP verdiend" + victory_gems_gained: "Juwelen verdiend" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Handleiding" tome_minion_spells: "Jouw Minions' Spreuken" # Only in old-style levels. tome_read_only_spells: "Read-Only Spreuken" # Only in old-style levels. @@ -239,31 +261,39 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # tome_cast_button_run: "Run" # tome_cast_button_running: "Running" # tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" + tome_submit_button: "Bevestig" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_select_method: "Kies voor een methode" +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Selecteer Iemand voor " tome_available_spells: "Beschikbare spreuken" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_your_skills: "Jouw vaardigheden" +# tome_help: "Help" + tome_current_method: "Huidige methode" + hud_continue_short: "Ga verder" + code_saved: "Code opgeslagen" skip_tutorial: "Overslaan (esc)" # keyboard_shortcuts: "Key Shortcuts" loading_ready: "Klaar!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" time_current: "Nu:" time_total: "Maximum:" time_goto: "Ga naar:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Probeer opnieuw" infinite_loop_reset_level: "Level resetten" infinite_loop_comment_out: "Mijn code weg commentariëren" tip_toggle_play: "Verwissel speel/pauze met Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ en Ctrl+] om terug te spoelen en vooruit te spoelen." + tip_scrub_shortcut: "Ctrl+[ en Ctrl+] om terug te spoelen en vooruit te spoelen." # {change} tip_guide_exists: "Klik op de handleiding bovenaan het scherm voor nuttige informatie." tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat lanceerde zijn beta versie in Oktober, 2013." tip_think_solution: "Denk aan de oplossing, niet aan het probleem." tip_theory_practice: "In theorie is er geen verschil tussen de theorie en de praktijk; in de praktijk is er wel een verschil. - Yogi Berra" @@ -285,46 +315,155 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: tip_impossible: "Het lijkt altijd onmogelijk tot het gedaan wordt. - Nelson Mandela" tip_talk_is_cheap: "Je kunt het goed uitleggen, maar toon me de code. - Linus Torvalds" tip_first_language: "Het ergste dat je kan leren is je eerste programmeertaal. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." + tip_hardware_problem: "Q: Hoeveel programmeurs zijn nodig om een lamp te vervangen? A: Geen, het is een hardware probleem." + tip_hofstadters_law: "Wet van Hofstadter: Het duurt altijd langer dan je verwacht, zelfs als je rekening houdt met de Wet van Hofstadter." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Pas Tovenaar aan" + tip_brute_force: "Als je twijfelt, gebruik brute kracht. - Ken Thompson" + tip_extrapolation: "Er zijn maar 2 soorten van mensen: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "Inventaris" + save_load_tab: "Bewaar/Laad" + options_tab: "Opties" + guide_tab: "Gids" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + inventory_caption: "Rust je held uit" + choose_hero_caption: "Kies held, taal" + save_load_caption: "... en bekijk geschiedenis" + options_caption: "Configure settings" + guide_caption: "Docs en tips" + multiplayer_caption: "Speel met vrienden!" + auth_caption: "Bewaar je vooruitgang." -# inventory: +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + + inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" +# required_purchase_title: "Required" + available_item: "Beschikbaar" + restricted_title: "Beperkt" # should_equip: "(double-click to equip)" # equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" + locked: "(gesloten)" + restricted: "(beperkt in dit level)" # equip: "Equip" # unequip: "Unequip" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + buy_gems: + few_gems: "Enkele juwelen" + pile_gems: "Een stapel juwelen" + chest_gems: "Een kist vol juwelen" + purchasing: "Kopen..." + declined: "Your card was declined" + retrying: "Server error, retrying." + prompt_title: "Niet genoeg juwelen" + prompt_body: "Wil je er meer verkrijgen?" + prompt_button: "Ga binnen in de winkel" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" + parents: "Voor ouders" + parents_title: "Uw kind zal de code leren." # {change} +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." editor_config: "Editor Configuratie" editor_config_title: "Editor Configuratie" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Nieuwe versie opslaan" new_major_version: "Nieuwe hoofd versie" +# submitting_patch: "Submitting Patch..." cla_prefix: "Om bewerkingen op te slaan, moet je eerst akkoord gaan met onze" cla_url: "CLA" cla_suffix: "." cla_agree: "IK GA AKKOORD" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contact opnemen met CodeCombat" welcome: "Goed om van je te horen! Gebruik dit formulier om ons een e-mail te sturen." - contribute_prefix: "Als je interesse hebt om bij te dragen, bekijk onze " - contribute_page: "pagina over bijdragen" - contribute_suffix: "!" forum_prefix: "Voor iets publiekelijks, probeer dan " forum_page: "ons forum" forum_suffix: "." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Feedback Verzonden" contact_candidate: "Contacteer Kandidaat" # Deprecated recruitment_reminder: "Gebruik dit formulier om kandidaten te contacteren voor wie je een interesse hebt om te interviewen. Vergeet niet dat CodeCombat een honorarium vraagt van 18% op het eerste-jaarssalaris. Dit honorarium moet betaald worden als de kandidaat wordt aangenomen en kon tot na 90 dagen terugbetaald worden als deze ontslagen wordt in deze periode. Deeltijds-, contract- en thuiswerkers worden van dit honorarium vrijgesteld, alsook interims." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: autosave: "Aanpassingen Automatisch Opgeslagen" me_tab: "Ik" picture_tab: "Afbeelding" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Wachtwoord" emails_tab: "Emails" admin: "Administrator" new_password: "Nieuw Wachtwoord" new_password_verify: "Verifieer" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "E-mail Abonnementen" # email_subscriptions_none: "No Email Subscriptions." email_announcements: "Aankondigingen" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: job_profile_explanation: "Hey! Vul dit in en we zullen je contacteren om je een job als softwareontwikkelaar te helpen vinden." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" - wizard_tab: "Tovenaar" - wizard_color: "Tovenaar Kleding Kleur" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "Tovenaar" archmage_title_description: "(Programmeur)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "Ambachtsman" artisan_title_description: "(Level Bouwer)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "Avonturier" adventurer_title_description: "(Level Tester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Klerk" scribe_title_description: "(Redacteur)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "Diplomaat" diplomat_title_description: "(Vertaler)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "Ambassadeur" ambassador_title_description: "(Ondersteuning)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: thang_title: "Thang Editor" level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" back: "Terug" revert: "Keer wijziging terug" revert_models: "keer wijziging model terug" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" fork_title: "Kloon naar nieuwe versie" fork_creating: "Kloon aanmaken..." # generate_terrain: "Generate Terrain" more: "Meer" wiki: "Wiki" live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" level_some_options: "Enkele opties?" level_tab_thangs: "Elementen" level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: level_tab_thangs_all: "Alles" level_tab_thangs_conditions: "Start Condities" level_tab_thangs_add: "Voeg element toe" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" delete: "Verwijder" duplicate: "Dupliceer" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" level_settings_title: "Instellingen" level_component_tab_title: "Huidige Componenten" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" article_search_title: "Zoek Artikels Hier" thang_search_title: "Zoek Thang Types Hier" level_search_title: "Zoek Levels Hier" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" read_only_warning2: "Pas op, je kunt geen aanpassingen opslaan hier, want je bent niet ingelogd." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Voorbeeld" edit_article_title: "Wijzig Artikel" +# polls: +# priority: "Priority" + contribute: page_title: "Bijdragen" - character_classes_title: "Karakterklassen" - introduction_desc_intro: "We hebben hoge verwachtingen over CodeCombat." - introduction_desc_pref: "We willen zijn waar programmeurs van alle niveaus komen om te leren en samen te spelen, anderen introduceren aan de wondere wereld van code, en de beste delen van de gemeenschap te reflecteren. We kunnen en willen dit niet alleen doen; wat projecten zoals GitHub, Stack Overflow en Linux groots en succesvol maken, zijn de mensen die deze software gebruiken en verbeteren. Daartoe, " - introduction_desc_github_url: "CodeCombat is volledig open source" - introduction_desc_suf: ", en we streven ernaar om op zoveel mogelijk manieren het mogelijk te maken voor u om deel te nemen en dit project van zowel jou als ons te maken." - introduction_desc_ending: "We hopen dat je met ons meedoet!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy en Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" alert_account_message_intro: "Hallo!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "Geïnteresserd in het werken aan game graphics, user interface design, database- en serverorganisatie, multiplayer networking, physics, geluid of game engine prestaties? Wil jij helpen een game te bouwen wat anderen leert waar jij goed in bent? We moeten nog veel doen en als jij een ervaren programmeur bent en wil ontwikkelen voor CodeCombat, dan is dit de klasse voor jou. We zouden graag je hulp hebben bij het maken van de beste programmeergame ooit." archmage_introduction: "Een van de beste aspecten aan het maken van spelletjes is dat zij zoveel verschillende zaken omvatten. Visualisaties, geluid, real-time netwerken, sociale netwerken, en natuurlijk enkele veelvoorkomende aspecten van programmeren, van low-level database beheer en server administratie tot gebruiksvriendelijke interfaces maken. Er is veel te doen, en als jij een ervaren programmeur bent met de motivatie om je volledig te verdiepen in de details van CodeCombat, dan ben je de tovenaar die wij zoeken! We zouden graag jouw hulp krijgen bij het bouwen van het allerbeste programmeerspel ooit." class_attributes: "Klasse kenmerken" archmage_attribute_1_pref: "Ervaring met " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: join_desc_4: "en we bekijken het verder vandaar!" join_url_email: "E-mail ons" join_url_hipchat: "ons publiek (Engelstalig) HipChat kanaal" - more_about_archmage: "Leer meer over hoe je een Machtige Tovenaar kan worden" archmage_subscribe_desc: "Ontvang e-mails met nieuwe programmeer mogelijkheden en aankondigingen." - artisan_summary_pref: "Wil je levels ontwerpen en CodeCombat's arsenaal vergroten? Mensen spelen sneller door onze content dan wij bij kunnen houden! Op dit moment is onze level editor nog wat beperkt, dus wees daarvan bewust. Het maken van levels zal een uitdaging zijn met een grote kans op fouten. Als jij een visie van campagnes hebt van for-loops tot" - artisan_summary_suf: ", dan is dit de klasse voor jou." artisan_introduction_pref: "We moeten meer levels bouwen! Mensen schreeuwen om meer inhoud, en er zijn ook maar zoveel levels dat wij kunnen maken. Momenteel is jouw werkplaats level een; onze level editor wordt zelfs door ons amper gebruikt, dus wees voorzichtig. Indien je een visie hebt van een campagne, gaande van for-loops tot" artisan_introduction_suf: ", dan is deze klasse waarschijnlijk iets voor jou." artisan_attribute_1: "Enige ervaring in het maken van vergelijkbare inhoud. Bijvoorbeeld ervaring in het gebruiken van Blizzard's level editor. Maar dit is niet vereist!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: artisan_join_step2: "Maak een nieuw level en bestudeer reeds bestaande levels." artisan_join_step3: "Praat met ons in ons publieke (Engelstalige) HipChat kanaal voor hulp. (optioneel)" artisan_join_step4: "Maak een bericht over jouw level op ons forum voor feedback." - more_about_artisan: "Leer meer over hoe je een Creatieve Ambachtsman kan worden." artisan_subscribe_desc: "Ontvang e-mails met nieuws over de Level Editor." - adventurer_summary: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels te proberen en te kijken hoe deze beter kunnen. Je zult veel afzien, want het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoog uihoudingsvermogen hebt, dan is dit de klasse voor jou." adventurer_introduction: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels uit te proberen en te kijken hoe deze beter kunnen. Je zult veel afzien.Het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoog uihoudingsvermogen hebt, dan is dit de klasse voor jou." adventurer_attribute_1: "Een wil om te leren. Jij wilt leren hoe je programmeert en wij willen het jou leren. Je zal overigens zelf het meeste leren doen." adventurer_attribute_2: "Charismatisch. Wees netjes maar duidelijk over wat er beter kan en geef suggesties over hoe het beter kan." adventurer_join_pref: "Werk samen met een Ambachtsman of recruteer er een, of tik het veld hieronder aan om e-mails te ontvangen wanneer er nieuwe levels zijn om te testen. We zullen ook berichten over levels die beoordeeld moeten worden op onze netwerken zoals" adventurer_forum_url: "ons forum" adventurer_join_suf: "dus als je liever op deze manier wordt geïnformeerd, schrijf je daar in!" - more_about_adventurer: "Leer meer over hoe je een Dappere Avonturier kunt worden." adventurer_subscribe_desc: "Ontvang e-mails wanneer er nieuwe levels zijn die getest moeten worden." - scribe_summary_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn die spelers kunnen nakijken. Op die manier zal een Ambachtsman een link kunnen geven naar een artikel dat past bij een level. Net zoiets als het " - scribe_summary_suf: " heeft gebouwd. Als jij het leuk vindt programmeerconcepten uit te leggen, dan is deze klasse iets voor jou." scribe_introduction_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn en een wiki met programmeerconcepten waar levels op in kunnen gaan. Op die manier zal niet elke Ambachtsman in detail hoeven uit te leggen wat een vergelijkingsoperator is, maar een link kunnen geven naar een artikel die deze informatie al verduidelijkt voor speler. Net zoiets als het " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " heeft gebouwd. Als jij het leuk vindt om programmeerconcepten uit te leggen in Markdown-vorm, dan is deze klasse wellicht iets voor jou." scribe_attribute_1: "Taalvaardigheid is praktisch alles wat je nodig hebt. Je moet niet enkel bedreven zijn in grammatica en spelling, maar ook moeilijke ideeën kunnen overbrengen aan anderen." contact_us_url: "Contacteer ons" scribe_join_description: "vertel ons wat over jezelf, je ervaring met programmeren en over wat voor soort dingen je graag zou schrijven. Verder zien we wel!" - more_about_scribe: "Leer meer over het worden van een ijverige Klerk." scribe_subscribe_desc: "Ontvang e-mails met aankondigingen over het schrijven van artikelen." - diplomat_summary: "Er is grote interesse voor CodeCombat in landen waar geen Engels wordt gesproken! We zijn op zoek naar vertalers die tijd willen spenderen aan het vertalen van de site's corpus aan woorden zodat CodeCombat zo snel mogelijk toegankelijk wordt voor de hele wereld. Als jij wilt helpen om CodeCombat internationaal maken, dan is dit de klasse voor jou." diplomat_introduction_pref: "Dus, als er iets is wat we geleerd hebben van de " diplomat_launch_url: "release in oktober" diplomat_introduction_suf: "dan is het wel dat er een enorme belangstelling is voor CodeCombat in andere landen, vooral Brazilië! We zijn een groep van vertalers aan het creëren dat ijverig de ene set woorden in de andere omzet om CodeCombat zo toegankelijk mogelijk te maken in de hele wereld. Als jij het leuk vindt glimpsen op te vangen van aankomende content en deze levels zo snel mogelijk naar je landgenoten te krijgen, dan is dit de klasse voor jou." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: diplomat_join_pref_github: "Vind van jouw taal het locale bestand " diplomat_github_url: "op GitHub" diplomat_join_suf_github: ", edit het online, en submit een pull request. Daarnaast kun je hieronder aanvinken als je up-to-date wilt worden gehouden met nieuwe internationalisatie-ontwikkelingen." - more_about_diplomat: "Leer meer over het worden van een geweldige Diplomaat" diplomat_subscribe_desc: "Ontvang e-mails over i18n ontwikkelingen en levels om te vertalen." - ambassador_summary: "We proberen een gemeenschap te bouwen en elke gemeenschap heeft een supportteam nodig wanneer er problemen zijn. We hebben chats, e-mails en sociale netwerken zodat onze gebruikers het spel kunnen leren kennen. Als jij mensen wilt helpen betrokken te raken, plezier te hebben en wat te leren programmeren, dan is dit wellicht de klasse voor jou." ambassador_introduction: "We zijn een gemeenschap aan het uitbouwen, en jij maakt er deel van uit. We hebben Olark chatkamers, emails, en sociale netwerken met veel andere mensen waarmee je kan praten en hulp aan kan vragen over het spel of om bij te leren. Als jij mensen wil helpen en te werken nabij de hartslag van CodeCombat in het bijsturen van onze toekomstvisie, dan is dit de geknipte klasse voor jou!" ambassador_attribute_1: "Communicatieskills. Problemen die spelers hebben kunnen identificeren en ze helpen deze op te lossen. Verder zul je ook de rest van ons geïnformeerd houden over wat de spelers zeggen, wat ze leuk vinden, wat ze minder vinden en waar er meer van moet zijn!" ambassador_join_desc: "vertel ons wat over jezelf, wat je hebt gedaan en wat je graag zou doen. We zien verder wel!" ambassador_join_note_strong: "Opmerking" ambassador_join_note_desc: "Een van onze topprioriteiten is om een multiplayer te bouwen waar spelers die moeite hebben een level op te lossen een tovenaar met een hoger level kunnen oproepen om te helpen. Dit zal een goede manier zijn voor ambassadeurs om hun ding te doen. We houden je op de hoogte!" - more_about_ambassador: "Leer meer over het worden van een behulpzame Ambassadeur" ambassador_subscribe_desc: "Ontvang e-mails met updates over ondersteuning en multiplayer-ontwikkelingen." changes_auto_save: "Veranderingen worden automatisch opgeslagen wanneer je het vierkantje aan- of afvinkt." diligent_scribes: "Onze ijverige Klerks:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "Fout bij het laden van de server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: leaderboard: "Scorebord" user_schema: "Gebruikersschema" user_profile: "Gebruikersprofiel" +# patch: "Patch" patches: "Patches" # patched_model: "Source Document" model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: practices_title: "Goede Respectvolle gewoonten" practices_description: "Dit zijn onze beloften aan u, de speler, in een iets minder juridische jargon." privacy_title: "Privacy" - privacy_description: "We zullen nooit jouw persoonlijke informatie verkopen. We willen in verloop van tijd geld verdienen dankzij aanwervingen, maar je mag op je beide oren slapen dat wij nooit jouw persoonlijke informatie zullen verspreiden aan geïnteresseerde bedrijven zonder dat jij daar expliciet mee akkoord gaat." +# privacy_description: "We will not sell any of your personal information." security_title: "Beveiliging" security_description: "We streven ernaar om jouw persoonlijke informatie veilig te bewaren. Onze website is open en beschikbaar voor iedereen, opdat ons beveiliging systeem kan worden nagekeken en geoptimaliseerd door iedereen die dat wil. Dit alles is mogelijk doordat we volledig open source en transparant zijn." email_title: "E-mail" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: email_description_suffix: "of via urls in de emails die wij verzenden, kan je jouw instellingen wijzigen en ten allen tijden uitschrijven." cost_title: "Kosten" cost_description: "Momenteel is CodeCombat 100% gratis! Één van onze doestellingen is om dit zo te houden, opdat zoveel mogelijk mensen kunnen spelen, onafhankelijk van waar je leeft of wie je bent. Als het financieel moeilijker wordt, kan het mogelijk zijn dat we gaan beginnen met abonnementen of een prijs zetten op bepaalde zaken, maar we streven ernaar om dit te voorkomen. Met een beetje geluk zullen we dit voor altijd kunnen garanderen met:" - recruitment_title: "Aanwervingen" - recruitment_description_prefix: "Hier bij CodeCombat, ga je ontplooien tot een krachtige tovenoor-niet enkel virtueel, maar ook in het echt." - url_hire_programmers: "Niemand kan snel genoeg programmeurs aanwerven" - recruitment_description_suffix: "dus eenmaal je jouw vaardigheden hebt aangescherp en ermee akkoord gaat, zullen we jouw beste programmeer prestaties voorstellen aan duizenden werkgevers die niet kunnen wachten om jou aan te werven. Zij betalen ons een beetje, maar betalen jou" - recruitment_description_italic: "enorm veel" - recruitment_description_ending: "de site blijft volledig gratis en iedereen is gelukkig. Dat is het plan." copyrights_title: "Auteursrechten en licenties" contributor_title: "Licentieovereenkomst voor vrijwilligers" contributor_description_prefix: "Alle bijdragen, zowel op de website als op onze GitHub repository, vallen onder onze" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Tovenaar instellingen" - customize_avatar: "Bewerk je avatar" - active: "Actief" - color: "Kleur" - group: "Groep" - clothes: "Kleren" - trim: "Trim" - cloud: "Wolk" - team: "Team" - spell: "Spreuk" - boots: "Laarzen" - hue: "Hue" - saturation: "Saturatie" - lightness: "Helderheid" - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/nl-NL.coffee b/app/locale/nl-NL.coffee index 83b7668dc..10456158f 100644 --- a/app/locale/nl-NL.coffee +++ b/app/locale/nl-NL.coffee @@ -3,14 +3,15 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription slogan: "Leer programmeren door het spelen van een spel" no_ie: "CodeCombat werkt niet in IE8 of ouder. Sorry!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat is niet gemaakt voor mobiele apparaten en werkt misschien niet!" # Warning that shows up on mobile devices - play: "Speel" # The big play button that just starts playing a level + play: "Speel" # The big play button that opens up the campaign view. old_browser: "Uh oh, jouw browser is te oud om CodeCombat te kunnen spelen, Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Je kan toch proberen, maar het zal waarschijnlijk niet werken!" + ipad_browser: "Slecht nieuws: CodeCombat draait niet in je browser op iPad. Goed nieuws: onze iPad-app wordt op het moment beoordeeld door Apple." campaign: "Campagne" for_beginners: "Voor Beginners" multiplayer: "Multiplayer" # Not currently shown on home page for_developers: "Voor ontwikkelaars" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Of download voor iPad" nav: play: "Levels" # The top nav bar entry where players choose which levels to play @@ -19,8 +20,8 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription blog: "Blog" forum: "Forum" account: "Lidmaatschap" -# profile: "Profile" -# stats: "Stats" + profile: "Profiel" + stats: "Statistieken" # code: "Code" admin: "Administrator" # Only shows up when you are an admin home: "Home" @@ -29,7 +30,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription about: "Over Ons" contact: "Contact" twitter_follow: "Volgen" -# teachers: "Teachers" + teachers: "Docenten" modal: close: "Sluiten" @@ -49,84 +50,84 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription play: play_as: "Speel als " # Ladder page spectate: "Toeschouwen" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play + players: "Spelers" # Hover over a level on /play + hours_played: "Speeltijd" # Hover over a level on /play # items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" + unlock: "Koop" # For purchasing items and heroes + confirm: "Bevestigen" + owned: "In bezit" # For items you own + locked: "Vergrendeld" + purchasable: "Te koop" # For a hero you unlocked but haven't purchased + available: "Beschikbaar" # skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play + heroes: "Helden" # Tooltip on hero shop button from /play + achievements: "Prestaties" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + settings: "Instellingen" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "Volgende" # Go from choose hero to choose inventory before playing a level + change_hero: "Verander held" # Go back from choose inventory to choose hero + choose_inventory: "Uitrusting kiezen" + buy_gems: "Edelstenen kopen" + subscription_required: "Abonnement nodig" + anonymous: "Anonieme Speler" level_difficulty: "Moeilijkheidsgraad: " campaign_beginner: "Beginnercampagne" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Kies Je Level" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Je kunt meteen naar een van de levels hieronder springen, of de levels bespreken op " - adventurer_forum: "het Avonturiersforum" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... waarin je de toverkunst van het programmeren leert." - campaign_dev: "Willekeurige moeilijkere levels" - campaign_dev_description: "... waarin je de interface leert kennen terwijl je wat moeilijkers doet." + awaiting_levels_adventurer_prefix: "We brengen 5 nieuwe levels per week uit." # {change} + awaiting_levels_adventurer: "Schrijf je in als Avonturier" + awaiting_levels_adventurer_suffix: "om de eerste te zijn die nieuwe levels speelt." + adjust_volume: "Volume aanpassen" campaign_multiplayer: "Multiplayer Arena's" campaign_multiplayer_description: "... waarin je direct tegen andere spelers speelt." - campaign_player_created: "Door-spelers-gemaakt" - campaign_player_created_description: "... waarin je ten strijde trekt tegen de creativiteit van andere <a href=\"/contribute#artisan\">Ambachtelijke Tovenaars</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "Account maken" log_in: "Inloggen" logging_in: "Bezig met inloggen" log_out: "Uitloggen" - recover: "account herstellen" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Wachtwoord vergeten?" + authenticate_gplus: "G+ verifiëren" + load_profile: "G+ profiel laden" + finishing: "Aan het afmaken" + sign_in_with_facebook: "Inloggen met Facebook" + sign_in_with_gplus: "Inloggen met G+" + signup_switch: "Wil je een account maken?" signup: - create_account_title: "Maak een account aan om je vooruitgang op te slaan" - description: "Het is gratis. We hebben maar een paar dingen nodig en dan kan je aan de slag:" email_announcements: "Ontvang aankondigingen via email" - coppa: "13+ of niet uit de VS" - coppa_why: "(Waarom?)" creating: "Account aanmaken..." sign_up: "Aanmelden" log_in: "inloggen met wachtwoord" social_signup: "Of je kunt je registreren met Facebook of G+:" -# required: "You need to log in before you can go that way." + required: "Je moet inloggen om daarheen te gaan." + login_switch: "Heb je al een account?" recover: recover_account_title: "Herstel Account" send_password: "Verzend nieuw wachtwoord" -# recovery_sent: "Recovery email sent." + recovery_sent: "Herstel e-mail verzonden." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Primair" + secondary: "Secundair" + armor: "Harnas" + accessories: "Accessoires" + misc: "Anders" + books: "Boeken" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Bezig met laden..." saving: "Opslaan..." sending: "Verzenden..." @@ -139,19 +140,37 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription fork: "Fork" play: "Spelen" # When used as an action verb, like "Play next level" retry: "Probeer opnieuw" + actions: "Acties" + info: "Info" + help: "Help" watch: "Volgen" unwatch: "Ontvolgen" submit_patch: "Correctie Opsturen" + submit_changes: "Veranderingen indienen" +# save_changes: "Save Changes" general: and: "en" name: "Naam" -# date: "Date" + date: "Datum" body: "Inhoud" version: "Versie" +# pending: "Pending" + accepted: "Geaccepteerd" + rejected: "Afgewezen" + withdrawn: "Teruggetrokken" + submitter: "Toevoeger" + submitted: "Toegevoegd" commit_msg: "Commit Bericht" + review: "Herzien" version_history: "Versie geschiedenis" version_history_for: "Versie geschiedenis voor: " + select_changes: "Selecteer hieronder twee veranderingen om het verschil te zien." +# undo_prefix: "Undo" + undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Speel voorproefje van dit level" result: "Resultaat" results: "Resultaten" description: "Beschrijving" @@ -173,7 +192,10 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription medium: "Medium" hard: "Moeilijk" player: "Speler" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Niveau" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Krijger" +# ranger: "Ranger" + wizard: "Tovenaar" units: second: "seconde" @@ -182,88 +204,96 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription minutes: "minuten" hour: "uur" hours: "uren" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + day: "dag" + days: "dagen" + week: "week" + weeks: "weken" + month: "maand" + months: "maanden" + year: "jaar" + years: "jaren" play_level: done: "Klaar" home: "Home" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "Level" # Like "Level: Dungeons of Kithgard" + skip: "Overslaan" + game_menu: "Spelmenu" guide: "Handleiding" restart: "Herstarten" goals: "Doelen" -# goal: "Goal" + goal: "Doel" # running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" + success: "Gelukt!" + incomplete: "Incompleet" + timed_out: "De tijd is op" # failing: "Failing" action_timeline: "Actie tijdlijn" click_to_select: "Klik op een eenheid om deze te selecteren." # control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_join_game: "Meespelen" + reload: "Herlaad" reload_title: "Alle Code Herladen?" reload_really: "Weet je zeker dat je dit level tot het begin wilt herladen?" reload_confirm: "Herlaad Alles" + victory: "Gewonnen" victory_title_prefix: "" victory_title_suffix: " Compleet" victory_sign_up: "Schrijf je in om je vooruitgang op te slaan" victory_sign_up_poke: "Wil je jouw code opslaan? Maak een gratis account aan!" victory_rate_the_level: "Beoordeel het level: " # Only in old-style levels. victory_return_to_ladder: "Keer terug naar de ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Speel Volgend Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "Ga door" + victory_saving_progress: "Voortgang opslaan" victory_go_home: "Ga naar Home" # Only in old-style levels. victory_review: "Vertel ons meer!" # Only in old-style levels. victory_hour_of_code_done: "Ben Je Klaar?" victory_hour_of_code_done_yes: "Ja, ik ben klaar met mijn Hour of Code!" + victory_experience_gained: "XP verdient" + victory_gems_gained: "Edelstenen verdient" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Handleiding" tome_minion_spells: "Jouw Minions' Spreuken" # Only in old-style levels. tome_read_only_spells: "Read-Only Spreuken" # Only in old-style levels. tome_other_units: "Andere Eenheden" # Only in old-style levels. -# tome_cast_button_run: "Run" + tome_cast_button_run: "Uitvoeren" # tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_ran: "Uitgevoerd" + tome_submit_button: "Indienen" + tome_reload_method: "Oorspronkelijke code voor deze methode herladen" # Title text for individual method reload button. + tome_select_method: "Kies een methode" + tome_see_all_methods: "Bekijk alle methoden die je kan aanpassen" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Selecteer Iemand voor " tome_available_spells: "Beschikbare spreuken" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_your_skills: "Jouw Vaardigheden" + tome_help: "Help" + tome_current_method: "Huidige Methode" + hud_continue_short: "Doorgaan" + code_saved: "Code Opgeslagen" skip_tutorial: "Overslaan (esc)" # keyboard_shortcuts: "Key Shortcuts" loading_ready: "Klaar!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" + loading_start: "Start Level" + problem_alert_title: "Verbeter je Code" + problem_alert_help: "Help" time_current: "Nu:" time_total: "Maximum:" time_goto: "Ga naar:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Probeer opnieuw" infinite_loop_reset_level: "Level resetten" infinite_loop_comment_out: "Mijn code weg commentariëren" tip_toggle_play: "Verwissel speel/pauze met Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ en Ctrl+] om terug te spoelen en vooruit te spoelen." + tip_scrub_shortcut: "Ctrl+[ en Ctrl+] om terug te spoelen en vooruit te spoelen." # {change} tip_guide_exists: "Klik op de handleiding bovenaan het scherm voor nuttige informatie." tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat lanceerde zijn beta versie in Oktober, 2013." tip_think_solution: "Denk aan de oplossing, niet aan het probleem." tip_theory_practice: "In theorie is er geen verschil tussen de theorie en de praktijk; in de praktijk is er wel een verschil. - Yogi Berra" @@ -285,53 +315,162 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription tip_impossible: "Het lijkt altijd onmogelijk tot het gedaan wordt. - Nelson Mandela" tip_talk_is_cheap: "Je kunt het goed uitleggen, maar toon me de code. - Linus Torvalds" tip_first_language: "Het ergste dat je kan leren is je eerste programmeertaal. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." + tip_hardware_problem: "Q: Hoeveel programmeurs heb je nodig om een lampje te vervangen? A: Nul, het is een a hardware probleem." + tip_hofstadters_law: "De Wet van Hofstadter: Het duurt altijd langer dan je verwacht, zelfs wanneer je rekening houdt met de Wet van Hofstadter." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Pas Tovenaar aan" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "Inventaris" + save_load_tab: "Opslaan/Laden" + options_tab: "Opties" + guide_tab: "Handleiding" + guide_video_tutorial: "Video Tutorial" + guide_tips: "Tips" multiplayer_tab: "Multiplayer" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" + auth_tab: "Inschrijven" + inventory_caption: "Kies de uitrusting van je held" + choose_hero_caption: "Kies held, taal" # save_load_caption: "... and view history" -# options_caption: "Configure settings" + options_caption: "Instellingen" # guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + multiplayer_caption: "Speel met vrienden!" + auth_caption: "Bewaar je voortgang." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "Kies Uitrusting" + equipped_item: "Gekozen" + required_purchase_title: "Verplicht" + available_item: "Beschikbaar" + restricted_title: "Onbeschikbaar" + should_equip: "(double-click om te kiezen)" + equipped: "(gekozen)" + locked: "(versleuteld)" + restricted: "(onbeschikbaar in dit level)" + equip: "Kies" + unequip: "Verwijder" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" + buy_gems: + few_gems: "Een paar edelstenen" + pile_gems: "Berg edelstenen" + chest_gems: "Schatkist met edelstenen" + purchasing: "Aan het kopen..." + declined: "Je kaart is geweigerd" + retrying: "Server fout, opnieuw aan het proberen." + prompt_title: "Niet genoeg edelstenen" + prompt_body: "Wil je meer krijgen?" + prompt_button: "Naar de winkel" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" + subscribe_title: "Abonneren" + unsubscribe: "Abonnement pzeggen" + confirm_unsubscribe: "Opzegging Bevestigen" + never_mind: "Laat Maar, Ik Hou Nog Steeds Van Je" + thank_you_months_prefix: "Bedankt voor je steun in de laatste" + thank_you_months_suffix: "maanden." + thank_you: "Bedankt dat je CodeCombat steunt." + sorry_to_see_you_go: "Jammer dat je weggaat! Laat ons alsjeblieft weten wat we beter hadden kunnen doen." + unsubscribe_feedback_placeholder: "O, wat hebben we gedaan?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" + parents: "Voor ouders" + parents_title: "Uw kind leert programmeren." # {change} + parents_blurb1: "Met CodeCombat leert uw kind door echte code te schrijven. Ze beginnen met simpele instructies en naarmate ze verder komen, komen er moeilijkere onderwerpen aan bod." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" + + choose_hero: + choose_hero: "Kies je held" + programming_language: "Programmeertaal" + programming_language_description: "Welke programmeertaal wil je gebruiken?" + default: "standaard" + experimental: "Experimenteel" # python_blurb: "Simple yet powerful, great for beginners and experts." # javascript_blurb: "The language of the web. (Not the same as Java.)" # coffeescript_blurb: "Nicer JavaScript syntax." @@ -339,17 +478,30 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" -# weapons: "Weapons" +# hero_type: "Type" + weapons: "Wapens" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" # weapons_wizard: "Wands, Staffs - Long Range, Magic" # attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" + health: "Gezondheid" + speed: "Snelheid" # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" + skills: "Vaardigheden" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." editor_config: "Editor Configuratie" editor_config_title: "Editor Configuratie" # editor_config_level_language_label: "Language for This Level" @@ -407,39 +557,133 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription why_paragraph_2_italic_caps: "NEE MAMA IK MOET DIT LEVEL AF MAKEN!" why_paragraph_2_suffix: "Dat is waarom CodeCombat een multiplayergame is, en niet zomaar lessen gegoten in spelformaat. We zullen niet stoppen totdat jij niet meer kan stoppen--maar deze keer, is dat iets goeds." why_paragraph_3: "Als je verslaafd gaat zijn aan een spel, dan is het beter om hieraan verslaafd te raken en een tovenaar van het technisch tijdperk te worden." -# press_title: "Bloggers/Press" -# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" -# press_paragraph_1_link: "press packet" -# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + press_title: "Bloggers/Journalisten" + press_paragraph_1_prefix: "Wil je over ons schrijven? Voel je vrij om de bestanden te gebruiken van ons" + press_paragraph_1_link: "journalistieke pakket" + press_paragraph_1_suffix: ". Alle logo's en afbeeldingen mogen worden gebruikt zonder direct contact met ons op te nemen." + team: "Team" + george_title: "CEO" # {change} + george_blurb: "Zaken type" + scott_title: "Programmeur" # {change} + scott_blurb: "de Redelijke" + nick_title: "Programmeur" # {change} + nick_blurb: "Motivatie Goeroe" + michael_title: "Programmeur" + michael_blurb: "Systeembeheerder" + matt_title: "Programmeur" # {change} + matt_blurb: "Fietser" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Nieuwe versie opslaan" new_major_version: "Nieuwe hoofd versie" +# submitting_patch: "Submitting Patch..." cla_prefix: "Om bewerkingen op te slaan, moet je eerst akkoord gaan met onze" cla_url: "CLA" cla_suffix: "." cla_agree: "IK GA AKKOORD" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contact opnemen met CodeCombat" welcome: "Goed om van je te horen! Gebruik dit formulier om ons een e-mail te sturen." - contribute_prefix: "Als je interesse hebt om bij te dragen, bekijk onze " - contribute_page: "pagina over bijdragen" - contribute_suffix: "!" forum_prefix: "Voor iets publiekelijks, probeer dan " forum_page: "ons forum" forum_suffix: "." + faq_prefix: "Er is ook een" + faq: "veelgestelde vragen sectie" + subscribe_prefix: "Als je hulp nodig hebt om een level te halen, overweeg dan alsjeblieft" + subscribe: "abonnee te worden van CodeCombat" + subscribe_suffix: ", dan helpen we je graag om je code te verbeteren." + subscriber_support: "Omdat je geabonneerd bent op CodeCombat, krijgt je email prioriteit bij het beantwoorden." + screenshot_included: "Screenshot inbegrepen." +# where_reply: "Where should we reply?" send: "Feedback Verzonden" contact_candidate: "Contacteer Kandidaat" # Deprecated recruitment_reminder: "Gebruik dit formulier om kandidaten te contacteren voor wie je een interesse hebt om te interviewen. Vergeet niet dat CodeCombat een honorarium vraagt van 18% op het eerste-jaarssalaris. Dit honorarium moet betaald worden als de kandidaat wordt aangenomen en kon tot na 90 dagen terugbetaald worden als deze ontslagen wordt in deze periode. Deeltijds-, contract- en thuiswerkers worden van dit honorarium vrijgesteld, alsook interims." # Deprecated @@ -450,21 +694,28 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription autosave: "Aanpassingen Automatisch Opgeslagen" me_tab: "Ik" picture_tab: "Afbeelding" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" upload_picture: "Je afbeelding opsturen" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Wachtwoord" emails_tab: "Emails" admin: "Administrator" new_password: "Nieuw Wachtwoord" new_password_verify: "Verifieer" - email_subscriptions: "E-mail Abonnementen" -# email_subscriptions_none: "No Email Subscriptions." +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." + email_subscriptions: "Email Abonnementen" + email_subscriptions_none: "Geen Email Abonnementen." email_announcements: "Aankondigingen" email_announcements_description: "Verkrijg emails over het laatste nieuws en de ontwikkelingen bij CodeCombat." email_notifications: "Notificaties" email_notifications_summary: "Instellingen voor gepersonaliseerde, automatische meldingen via e-mail omtrent je activiteit op CodeCombat." email_any_notes: "Alle Meldingen" - email_any_notes_description: "Zet alle activiteit-meldingen via e-mail af." -# email_news: "News" + email_any_notes_description: "Zet alle activiteit-meldingen via email af." + email_news: "Nieuws" email_recruit_notes: "Job Aanbiedingen" email_recruit_notes_description: "Als je zeer goed speelt, zouden we je wel eens kunnen contacteren om je een (betere) job aan te bieden." contributor_emails: "Medewerker Klasse emails" @@ -475,19 +726,18 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription error_saving: "Fout Tijdens Het Opslaan" saved: "Aanpassingen Opgeslagen" password_mismatch: "Het wachtwoord komt niet overeen." -# password_repeat: "Please repeat your password." + password_repeat: "Herhaal je wachtwoord." job_profile: "Job Profiel" # Rest of this section (the job profile stuff and wizard stuff) is deprecated job_profile_approved: "Jouw job profiel werd goedgekeurd door CodeCombat. Werkgevers zullen het kunnen bekijken totdat je het inactief zet of als er geen verandering in komt voor vier weken." job_profile_explanation: "Hey! Vul dit in en we zullen je contacteren om je een job als softwareontwikkelaar te helpen vinden." sample_profile: "Bekijk een voorbeeld kandidaat-profiel" view_profile: "Bekijk je eigen kandidaat-profiel" - wizard_tab: "Tovenaar" - wizard_color: "Tovenaar Kleding Kleur" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." community: main_title: "CodeCombat Gemeenschap" @@ -523,52 +772,117 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "Tovenaar" archmage_title_description: "(Programmeur)" + archmage_summary: "Als je een programmeur met interesse in het maken van educatieve spellen, word dan Tovernaar en help ons CodeCombat te ontwikkelen!" artisan_title: "Ambachtsman" artisan_title_description: "(Level Bouwer)" + artisan_summary: "Bouw en deel levels die je alleen of met vrienden kan spelen. Word een Ambachtsman om anderen te helpen leren programmeren." adventurer_title: "Avonturier" adventurer_title_description: "(Level Tester)" + adventurer_summary: "Krijg gratis een week eerder toegang tot nieuwe levels (zelfs levels voor abonnees) en help ons bugs te vinden voordat het level uitgebracht wordt." scribe_title: "Klerk" scribe_title_description: "(Redacteur)" + scribe_summary: "Goede code heeft goede documentatie nodig. Schrijf, wijzig en verbeter de documentatie die door wereldwijd door miljoenen spelers wordt gelezen." diplomat_title: "Diplomaat" diplomat_title_description: "(Vertaler)" + diplomat_summary: "CodeCombat ondersteunt meer dan 45 talen. Word Diplomaat en help met vertalen." ambassador_title: "Ambassadeur" ambassador_title_description: "(Ondersteuning)" + ambassador_summary: "Bewaar de vrede op onze forums en help mensen met vragen. Onze ambassadeurs helpen CodeCombat te presenteren aan de buitenwereld." editor: main_title: "CodeCombat Editors" article_title: "Artikel Editor" thang_title: "Thang Editor" level_title: "Level Editor" -# achievement_title: "Achievement Editor" + achievement_title: "Prestatie Editor" +# poll_title: "Poll Editor" back: "Terug" revert: "Keer wijziging terug" revert_models: "keer wijziging model terug" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" + pick_a_terrain: "Kies een Terrein" + dungeon: "Kelder" + indoor: "Binnen" + desert: "Woestijn" + grassy: "Grassig" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Klein" + large: "Groot" fork_title: "Kloon naar nieuwe versie" fork_creating: "Kloon aanmaken..." -# generate_terrain: "Generate Terrain" + generate_terrain: "Genereer Terrein" more: "Meer" wiki: "Wiki" live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" + thang_colors: "Kleuren" level_some_options: "Enkele opties?" level_tab_thangs: "Elementen" level_tab_scripts: "Scripts" level_tab_settings: "Instellingen" level_tab_components: "Componenten" level_tab_systems: "Systemen" -# level_tab_docs: "Documentation" + level_tab_docs: "Documentatie" level_tab_thangs_title: "Huidige Elementen" level_tab_thangs_all: "Alles" level_tab_thangs_conditions: "Start Condities" level_tab_thangs_add: "Voeg element toe" +# level_tab_thangs_search: "Search thangs" + add_components: "Voeg Componenten Toe" + component_configs: "Componenten Configuraties" + config_thang: "Double click om een thang te configureren" delete: "Verwijder" duplicate: "Dupliceer" -# rotate: "Rotate" + stop_duplicate: "Stop Dupliceren" + rotate: "Roteer" level_settings_title: "Instellingen" level_component_tab_title: "Huidige Componenten" level_component_btn_new: "Maak een nieuwe component aan" @@ -587,38 +901,41 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription new_article_title: "Maak een Nieuw Artikel" new_thang_title: "Maak een Nieuw Thang Type" new_level_title: "Maak een Nieuw Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" - article_search_title: "Zoek Artikels Hier" + new_article_title_login: "Log In om een Nieuw Artikel te maken" + new_thang_title_login: "Log In om een Nieuw Thang Type te maken" + new_level_title_login: "Log In om een New Level te maken" + new_achievement_title: "Maak een Nieuwe Prestatie" + new_achievement_title_login: "Log In om een Nieuwe Prestatie te maken" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" + article_search_title: "Zoek Artikelen Hier" thang_search_title: "Zoek Thang Types Hier" level_search_title: "Zoek Levels Hier" -# achievement_search_title: "Search Achievements" + achievement_search_title: "Zoek Prestaties" +# poll_search_title: "Search Polls" read_only_warning2: "Pas op, je kunt geen aanpassingen opslaan hier, want je bent niet ingelogd." -# no_achievements: "No achievements have been added for this level yet." + no_achievements: "Er zijn nog geen achievements toegevoegd voor dit level" # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" + level_completion: "Level Voltooiing" # pop_i18n: "Populate I18N" + tasks: "Taken" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Voorbeeld" edit_article_title: "Wijzig Artikel" +# polls: +# priority: "Priority" + contribute: page_title: "Bijdragen" - character_classes_title: "Karakterklassen" - introduction_desc_intro: "We hebben hoge verwachtingen over CodeCombat." - introduction_desc_pref: "We willen zijn waar programmeurs van alle niveaus komen om te leren en samen te spelen, anderen introduceren aan de wondere wereld van code, en de beste delen van de gemeenschap te reflecteren. We kunnen en willen dit niet alleen doen; wat projecten zoals GitHub, Stack Overflow en Linux groots en succesvol maken, zijn de mensen die deze software gebruiken en verbeteren. Daartoe, " - introduction_desc_github_url: "CodeCombat is volledig open source" - introduction_desc_suf: ", en we streven ernaar om op zoveel mogelijk manieren het mogelijk te maken voor u om deel te nemen en dit project van zowel jou als ons te maken." - introduction_desc_ending: "We hopen dat je met ons meedoet!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy en Matt" + intro_blurb: "CodeCombat is 100% open source! Honderden toegewijde spelers hebben ons geholpen bij het maken van dit spel. Doe mee en help CodeCombat in zijn epische missie de wereld te leren programmeren!" alert_account_message_intro: "Hallo!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "Geïnteresserd in het werken aan game graphics, user interface design, database- en serverorganisatie, multiplayer networking, physics, geluid of game engine prestaties? Wil jij helpen een game te bouwen wat anderen leert waar jij goed in bent? We moeten nog veel doen en als jij een ervaren programmeur bent en wil ontwikkelen voor CodeCombat, dan is dit de klasse voor jou. We zouden graag je hulp hebben bij het maken van de beste programmeergame ooit." + alert_account_message: "Om je aan te melden voor klasse emails, moest je eerst inloggen." archmage_introduction: "Een van de beste aspecten aan het maken van spelletjes is dat zij zoveel verschillende zaken omvatten. Visualisaties, geluid, real-time netwerken, sociale netwerken, en natuurlijk enkele veelvoorkomende aspecten van programmeren, van low-level database beheer en server administratie tot gebruiksvriendelijke interfaces maken. Er is veel te doen, en als jij een ervaren programmeur bent met de motivatie om je volledig te verdiepen in de details van CodeCombat, dan ben je de tovenaar die wij zoeken! We zouden graag jouw hulp krijgen bij het bouwen van het allerbeste programmeerspel ooit." class_attributes: "Klasse kenmerken" archmage_attribute_1_pref: "Ervaring met " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription join_desc_4: "en we bekijken het verder vandaar!" join_url_email: "E-mail ons" join_url_hipchat: "ons publiek (Engelstalig) HipChat kanaal" - more_about_archmage: "Leer meer over hoe je een Machtige Tovenaar kan worden" archmage_subscribe_desc: "Ontvang e-mails met nieuwe programmeer mogelijkheden en aankondigingen." - artisan_summary_pref: "Wil je levels ontwerpen en CodeCombat's arsenaal vergroten? Mensen spelen sneller door onze content dan wij bij kunnen houden! Op dit moment is onze level editor nog wat beperkt, dus wees daarvan bewust. Het maken van levels zal een uitdaging zijn met een grote kans op fouten. Als jij een visie van campagnes hebt van for-loops tot" - artisan_summary_suf: ", dan is dit de klasse voor jou." artisan_introduction_pref: "We moeten meer levels bouwen! Mensen schreeuwen om meer inhoud, en er zijn ook maar zoveel levels dat wij kunnen maken. Momenteel is jouw werkplaats level een; onze level editor wordt zelfs door ons amper gebruikt, dus wees voorzichtig. Indien je een visie hebt van een campagne, gaande van for-loops tot" artisan_introduction_suf: ", dan is deze klasse waarschijnlijk iets voor jou." artisan_attribute_1: "Enige ervaring in het maken van vergelijkbare inhoud. Bijvoorbeeld ervaring in het gebruiken van Blizzard's level editor. Maar dit is niet vereist!" @@ -645,47 +959,37 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription artisan_join_step2: "Maak een nieuw level en bestudeer reeds bestaande levels." artisan_join_step3: "Praat met ons in ons publieke (Engelstalige) HipChat kanaal voor hulp. (optioneel)" artisan_join_step4: "Maak een bericht over jouw level op ons forum voor feedback." - more_about_artisan: "Leer meer over hoe je een Creatieve Ambachtsman kan worden." artisan_subscribe_desc: "Ontvang e-mails met nieuws over de Level Editor." - adventurer_summary: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels te proberen en te kijken hoe deze beter kunnen. Je zult veel afzien, want het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoog uihoudingsvermogen hebt, dan is dit de klasse voor jou." adventurer_introduction: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels uit te proberen en te kijken hoe deze beter kunnen. Je zult veel afzien.Het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoog uihoudingsvermogen hebt, dan is dit de klasse voor jou." adventurer_attribute_1: "Een wil om te leren. Jij wilt leren hoe je programmeert en wij willen het jou leren. Je zal overigens zelf het meeste leren doen." adventurer_attribute_2: "Charismatisch. Wees netjes maar duidelijk over wat er beter kan en geef suggesties over hoe het beter kan." adventurer_join_pref: "Werk samen met een Ambachtsman of recruteer er een, of tik het veld hieronder aan om e-mails te ontvangen wanneer er nieuwe levels zijn om te testen. We zullen ook berichten over levels die beoordeeld moeten worden op onze netwerken zoals" adventurer_forum_url: "ons forum" adventurer_join_suf: "dus als je liever op deze manier wordt geïnformeerd, schrijf je daar in!" - more_about_adventurer: "Leer meer over hoe je een Dappere Avonturier kunt worden." adventurer_subscribe_desc: "Ontvang e-mails wanneer er nieuwe levels zijn die getest moeten worden." - scribe_summary_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn die spelers kunnen nakijken. Op die manier zal een Ambachtsman een link kunnen geven naar een artikel dat past bij een level. Net zoiets als het " - scribe_summary_suf: " heeft gebouwd. Als jij het leuk vindt programmeerconcepten uit te leggen, dan is deze klasse iets voor jou." scribe_introduction_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn en een wiki met programmeerconcepten waar levels op in kunnen gaan. Op die manier zal niet elke Ambachtsman in detail hoeven uit te leggen wat een vergelijkingsoperator is, maar een link kunnen geven naar een artikel die deze informatie al verduidelijkt voor speler. Net zoiets als het " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " heeft gebouwd. Als jij het leuk vindt om programmeerconcepten uit te leggen in Markdown-vorm, dan is deze klasse wellicht iets voor jou." scribe_attribute_1: "Taalvaardigheid is praktisch alles wat je nodig hebt. Je moet niet enkel bedreven zijn in grammatica en spelling, maar ook moeilijke ideeën kunnen overbrengen aan anderen." contact_us_url: "Contacteer ons" scribe_join_description: "vertel ons wat over jezelf, je ervaring met programmeren en over wat voor soort dingen je graag zou schrijven. Verder zien we wel!" - more_about_scribe: "Leer meer over het worden van een ijverige Klerk." scribe_subscribe_desc: "Ontvang e-mails met aankondigingen over het schrijven van artikelen." - diplomat_summary: "Er is grote interesse voor CodeCombat in landen waar geen Engels wordt gesproken! We zijn op zoek naar vertalers die tijd willen spenderen aan het vertalen van de site's corpus aan woorden zodat CodeCombat zo snel mogelijk toegankelijk wordt voor de hele wereld. Als jij wilt helpen om CodeCombat internationaal maken, dan is dit de klasse voor jou." diplomat_introduction_pref: "Dus, als er iets is wat we geleerd hebben van de " diplomat_launch_url: "release in oktober" diplomat_introduction_suf: "dan is het wel dat er een enorme belangstelling is voor CodeCombat in andere landen, vooral Brazilië! We zijn een groep van vertalers aan het creëren dat ijverig de ene set woorden in de andere omzet om CodeCombat zo toegankelijk mogelijk te maken in de hele wereld. Als jij het leuk vindt glimpsen op te vangen van aankomende content en deze levels zo snel mogelijk naar je landgenoten te krijgen, dan is dit de klasse voor jou." diplomat_attribute_1: "Vloeiend Engels en de taal waar naar je wilt vertalen kunnen spreken. Wanneer je moeilijke ideeën wilt overbrengen, is het belangrijk beide talen goed te begrijpen!" # diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" + diplomat_i18n_page: "vertalingen pagina" # diplomat_i18n_page_suffix: ", or our interface and website on GitHub." diplomat_join_pref_github: "Vind van jouw taal het locale bestand " diplomat_github_url: "op GitHub" diplomat_join_suf_github: ", edit het online, en submit een pull request. Daarnaast kun je hieronder aanvinken als je up-to-date wilt worden gehouden met nieuwe internationalisatie-ontwikkelingen." - more_about_diplomat: "Leer meer over het worden van een geweldige Diplomaat" diplomat_subscribe_desc: "Ontvang e-mails over i18n ontwikkelingen en levels om te vertalen." - ambassador_summary: "We proberen een gemeenschap te bouwen en elke gemeenschap heeft een supportteam nodig wanneer er problemen zijn. We hebben chats, e-mails en sociale netwerken zodat onze gebruikers het spel kunnen leren kennen. Als jij mensen wilt helpen betrokken te raken, plezier te hebben en wat te leren programmeren, dan is dit wellicht de klasse voor jou." ambassador_introduction: "We zijn een gemeenschap aan het uitbouwen, en jij maakt er deel van uit. We hebben Olark chatkamers, emails, en sociale netwerken met veel andere mensen waarmee je kan praten en hulp aan kan vragen over het spel of om bij te leren. Als jij mensen wil helpen en te werken nabij de hartslag van CodeCombat in het bijsturen van onze toekomstvisie, dan is dit de geknipte klasse voor jou!" ambassador_attribute_1: "Communicatieskills. Problemen die spelers hebben kunnen identificeren en ze helpen deze op te lossen. Verder zul je ook de rest van ons geïnformeerd houden over wat de spelers zeggen, wat ze leuk vinden, wat ze minder vinden en waar er meer van moet zijn!" ambassador_join_desc: "vertel ons wat over jezelf, wat je hebt gedaan en wat je graag zou doen. We zien verder wel!" ambassador_join_note_strong: "Opmerking" ambassador_join_note_desc: "Een van onze topprioriteiten is om een multiplayer te bouwen waar spelers die moeite hebben een level op te lossen een tovenaar met een hoger level kunnen oproepen om te helpen. Dit zal een goede manier zijn voor ambassadeurs om hun ding te doen. We houden je op de hoogte!" - more_about_ambassador: "Leer meer over het worden van een behulpzame Ambassadeur" ambassador_subscribe_desc: "Ontvang e-mails met updates over ondersteuning en multiplayer-ontwikkelingen." changes_auto_save: "Veranderingen worden automatisch opgeslagen wanneer je het vierkantje aan- of afvinkt." diligent_scribes: "Onze ijverige Klerks:" @@ -720,12 +1024,12 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription rank_failed: "Beoordeling mislukt" rank_being_ranked: "Spel wordt Beoordeeld" # rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" + help_simulate: "Helpen spellen te simuleren?" code_being_simulated: "Uw nieuwe code wordt gesimuleerd door andere spelers om te beoordelen. Dit wordt vernieuwd zodra nieuwe matches binnenkomen." no_ranked_matches_pre: "Geen beoordeelde wedstrijden voor het" no_ranked_matches_post: " team! Speel tegen enkele tegenstanders en kom terug hier om uw spel te laten beoordelen." choose_opponent: "Kies een tegenstander" -# select_your_language: "Select your language!" + select_your_language: "Kies je taal!" tutorial_play: "Speel de Tutorial" tutorial_recommended: "Aanbevolen als je nog niet eerder hebt gespeeld" tutorial_skip: "Sla Tutorial over" @@ -734,57 +1038,89 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription simple_ai: "Simpele AI" warmup: "Opwarming" friends_playing: "Spelende Vrienden" -# log_in_for_friends: "Log in to play with your friends!" + log_in_for_friends: "Log in om met je vrienden te spelen!" social_connect_blurb: "Koppel je sociaal netwerk om tegen je vrienden te spelen!" invite_friends_to_battle: "Nodig je vrienden uit om deel te nemen aan het gevecht!" fight: "Aanvallen!" watch_victory: "Aanschouw je overwinning!" defeat_the: "Versla de" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" +# tournament_started: ", started" + tournament_ends: "Toernooi eindigt" + tournament_ended: "Toernooi geeindigd" + tournament_rules: "Toernooi regels" + tournament_blurb: "Schrijf code, verzamel goud, bouw legers, verpletter je tegenstanders, win prijzen en verbeter je carrière in ons $40,000 Greed toernooi! Zie de details" + tournament_blurb_criss_cross: "Win biedingen, bouw paden, wees je tegenstander te slim af, pak edelstenen en verbeter je carrière in ons Criss-Cross toernooi! Zie de details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" + tournament_blurb_blog: "op ons blog" + rules: "Regels" + winners: "Winnaars" -# user: -# stats: "Stats" + user: + stats: "Statistieken" # singleplayer_title: "Singleplayer Levels" # multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" + achievements_title: "Prestaties" + last_played: "Laatst Gespeeld" # status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + status_completed: "Voltooid" + status_unfinished: "Onvoltooid" + no_singleplayer: "Nog geen Singleplayer spellen gespeeld." + no_multiplayer: "Nog geen Multiplayer spellen gespeeld." + no_achievements: "Nog geen Prestaties verdiend." + favorite_prefix: "Favoriete taal is " + favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" + achievements: + last_earned: "Laatst verdiend" + amount_achieved: "Hoeveelheid" + achievement: "Prestatie" # category_contributor: "Contributor" # category_ladder: "Ladder" # category_level: "Level" -# category_miscellaneous: "Miscellaneous" + category_miscellaneous: "Diversen" # category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" + category_undefined: "Geen categorie" + current_xp_prefix: "" # current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + new_xp_prefix: "" + new_xp_postfix: " verdiend" + left_xp_prefix: "" + left_xp_infix: " tot level " + left_xp_postfix: "" # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "Fout bij het laden van de server" @@ -800,7 +1136,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription unknown: "Onbekende fout." resources: -# sessions: "Sessions" + sessions: "Sessies" your_sessions: "Jouw sessies." level: "Level" social_network_apis: "Sociale netwerk APIs" @@ -812,11 +1148,12 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription leaderboard: "Scorebord" user_schema: "Gebruikersschema" user_profile: "Gebruikersprofiel" + patch: "Patch" patches: "Patches" # patched_model: "Source Document" model: "Model" system: "Systeem" -# systems: "Systems" + systems: "Systemen" component: "Component" components: "Componenten" thang: "Thang" @@ -825,29 +1162,56 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription opponent_session: "Sessie van tegenstander" article: "Artikel" user_names: "Gebruikersnamen" -# thang_names: "Thang Names" + thang_names: "Thang Namen" files: "Bestanden" top_simulators: "Top Simulatoren" # source_document: "Source Document" -# document: "Document" + document: "Document" # sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" + employers: "Werkgevers" + candidates: "Candidaten" # candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" + user_remark: "Gebruiker Opmerking" + user_remarks: "Gebruiken Opmerkingen" + versions: "Versies" + items: "Items" +# hero: "Hero" + heroes: "Helden" + achievement: "Prestatie" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" + payment_info: "Betalings Informatie" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription practices_title: "Goede Respectvolle gewoonten" practices_description: "Dit zijn onze beloften aan u, de speler, in een iets minder juridische jargon." privacy_title: "Privacy" - privacy_description: "We zullen nooit jouw persoonlijke informatie verkopen. We willen in verloop van tijd geld verdienen dankzij aanwervingen, maar je mag op je beide oren slapen dat wij nooit jouw persoonlijke informatie zullen verspreiden aan geïnteresseerde bedrijven zonder dat jij daar expliciet mee akkoord gaat." + privacy_description: "We zullen je persoonlijke informatie niet verkopen." security_title: "Beveiliging" security_description: "We streven ernaar om jouw persoonlijke informatie veilig te bewaren. Onze website is open en beschikbaar voor iedereen, opdat ons beveiliging systeem kan worden nagekeken en geoptimaliseerd door iedereen die dat wil. Dit alles is mogelijk doordat we volledig open source en transparant zijn." email_title: "E-mail" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription email_description_suffix: "of via urls in de emails die wij verzenden, kan je jouw instellingen wijzigen en ten allen tijden uitschrijven." cost_title: "Kosten" cost_description: "Momenteel is CodeCombat 100% gratis! Één van onze doestellingen is om dit zo te houden, opdat zoveel mogelijk mensen kunnen spelen, onafhankelijk van waar je leeft of wie je bent. Als het financieel moeilijker wordt, kan het mogelijk zijn dat we gaan beginnen met abonnementen of een prijs zetten op bepaalde zaken, maar we streven ernaar om dit te voorkomen. Met een beetje geluk zullen we dit voor altijd kunnen garanderen met:" - recruitment_title: "Aanwervingen" - recruitment_description_prefix: "Hier bij CodeCombat, ga je ontplooien tot een krachtige tovenoor-niet enkel virtueel, maar ook in het echt." - url_hire_programmers: "Niemand kan snel genoeg programmeurs aanwerven" - recruitment_description_suffix: "dus eenmaal je jouw vaardigheden hebt aangescherp en ermee akkoord gaat, zullen we jouw beste programmeer prestaties voorstellen aan duizenden werkgevers die niet kunnen wachten om jou aan te werven. Zij betalen ons een beetje, maar betalen jou" - recruitment_description_italic: "enorm veel" - recruitment_description_ending: "de site blijft volledig gratis en iedereen is gelukkig. Dat is het plan." copyrights_title: "Auteursrechten en licenties" contributor_title: "Licentieovereenkomst voor vrijwilligers" contributor_description_prefix: "Alle bijdragen, zowel op de website als op onze GitHub repository, vallen onder onze" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Tovenaar instellingen" - customize_avatar: "Bewerk je avatar" - active: "Actief" - color: "Kleur" - group: "Groep" - clothes: "Kleren" - trim: "Trim" - cloud: "Wolk" - team: "Team" - spell: "Spreuk" - boots: "Laarzen" - hue: "Hue" - saturation: "Saturatie" - lightness: "Helderheid" - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/nn.coffee b/app/locale/nn.coffee index 227eaeaf9..75bebe204 100644 --- a/app/locale/nn.coffee +++ b/app/locale/nn.coffee @@ -1,11 +1,12 @@ -module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "Norwegian", translation: +module.exports = nativeDescription: "Norsk Nynorsk", englishDescription: "Norwegian (Nynorsk)", translation: # home: # slogan: "Learn to Code by Playing a Game" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level +# play: "Play" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" # login: # sign_up: "Create Account" # log_in: "Log In" # logging_in: "Logging In" # log_out: "Log Out" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Loading..." # saving: "Saving..." # sending: "Sending..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # fork: "Fork" # play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/pl.coffee b/app/locale/pl.coffee index b5cd7f9a8..714a31ea9 100644 --- a/app/locale/pl.coffee +++ b/app/locale/pl.coffee @@ -1,19 +1,20 @@ -module.exports = nativeDescription: "język polski", englishDescription: "Polish", translation: +module.exports = nativeDescription: "polski", englishDescription: "Polish", translation: home: slogan: "Naucz się programowania grając" - no_ie: "CodeCombat nie działa na Internet Explorer 8 lub starszym. Przepraszamy!" # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat nie został zaprojektowany dla urządzeń przenośnych więc może nie działać!" # Warning that shows up on mobile devices - play: "Graj" # The big play button that just starts playing a level + no_ie: "CodeCombat nie działa na Internet Explorer 8 i starszych. Przepraszamy!" # Warning that only shows up in IE8 and older + no_mobile: "CodeCombat nie został zaprojektowany dla urządzeń przenośnych, więc mogą występować pewne problemy w jego działaniu!" # Warning that shows up on mobile devices + play: "Graj" # The big play button that opens up the campaign view. old_browser: "Wygląda na to, że twoja przeglądarka jest zbyt stara, by obsłużyć CodeCombat. Wybacz!" # Warning that shows up on really old Firefox/Chrome/Safari - old_browser_suffix: "Możesz spróbowac mimo tego, ale prawdopodobnie gra nie będzie działać." + old_browser_suffix: "Mimo tego możesz spróbowac, ale prawdopodobnie gra nie będzie działać." + ipad_browser: "Zła wiadomość: CodeCombat nie działa na przeglądarce w iPadzie. Dobra wiadomość: nasza aplikacja na iPada czeka na akceptację od Apple." campaign: "Kampania" for_beginners: "Dla początkujących" -# multiplayer: "Multiplayer" # Not currently shown on home page + multiplayer: "Multiplayer" # Not currently shown on home page for_developers: "Dla developerów" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Albo ściągnij na swojego iPada" nav: - play: "Graj" # The top nav bar entry where players choose which levels to play + play: "Poziomy" # The top nav bar entry where players choose which levels to play community: "Społeczność" editor: "Edytor" blog: "Blog" @@ -29,7 +30,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish about: "O nas" contact: "Kontakt" twitter_follow: "Subskrybuj" -# teachers: "Teachers" + teachers: "Nauczyciele" modal: close: "Zamknij" @@ -51,107 +52,125 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish spectate: "Oglądaj" # Ladder page players: "graczy" # Hover over a level on /play hours_played: "rozegranych godzin" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + items: "Przedmioty" # Tooltip on item shop button from /play + unlock: "Odblokuj" # For purchasing items and heroes + confirm: "Potwierdź" + owned: "Posiadane" # For items you own + locked: "Zablokowane" + purchasable: "Można kupić" # For a hero you unlocked but haven't purchased + available: "Dostępny" + skills_granted: "Zdobyte umiejętności" # Property documentation details + heroes: "Bohaterowie" # Tooltip on hero shop button from /play + achievements: "Osiągnięcia" # Tooltip on achievement list button from /play + account: "Konto" # Tooltip on account button from /play + settings: "Opcje" # Tooltip on settings button from /play + poll: "Ankieta" # Tooltip on poll button from /play + next: "Dalej" # Go from choose hero to choose inventory before playing a level + change_hero: "Wybierz bohatera" # Go back from choose inventory to choose hero + choose_inventory: "Załóż przedmioty" + buy_gems: "Kup klejnoty" + subscription_required: "Wymagana subskrypcja" + anonymous: "Anonimowy gracz" level_difficulty: "Poziom trudności: " campaign_beginner: "Kampania dla początkujących" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Wybierz poziom" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Możesz wybrać jeden z poniższych poziomów lub omówić poziom na " - adventurer_forum: "forum Podróżników" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... w której nauczysz się magii programowania" - campaign_dev: "Losowe trudniejsze poziomy" - campaign_dev_description: "... w których nauczysz się interfejsu robiąc coś trudniejszego." - campaign_multiplayer: "Pola walki dla wielu graczy" - campaign_multiplayer_description: "... w których konkurujesz z innymi graczami." - campaign_player_created: "Stworzone przez graczy" - campaign_player_created_description: "... w których walczysz przeciwko dziełom <a href=\"/contribute#artisan\">Czarodziejów Rzemieślników</a>" - campaign_classic_algorithms: "Algorytmy klasyczne" - campaign_classic_algorithms_description: "... gdzie nauczysz się najpopularniejszych alogrytmów w Informatyce." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" + awaiting_levels_adventurer_prefix: "Wydajemy pięć poziomów na tydzień." # {change} + awaiting_levels_adventurer: "Zapisz się jako Podróżnik," + awaiting_levels_adventurer_suffix: "aby jako pierwszy grać w nowe poziomy." + adjust_volume: "Dopasuj głośność" + campaign_multiplayer: "Kampania dla wielu graczy" + campaign_multiplayer_description: "... w której konkurujesz z innymi graczami." +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Robisz coraz to większe postępy! Pokaż swoim rodzicom jak wiele nauczyłeś się z CodeCombat." + email_invalid: "Błędny adres e-mail." + form_blurb: "Wpisz adres email swich rodziców, a my ich o tym poinformujemy!" + form_label: "Adres email" + placeholder: "adres email" + title: "Wspaniała robota, Adepcie" login: sign_up: "Stwórz konto" log_in: "Zaloguj się" logging_in: "Logowanie..." log_out: "Wyloguj się" - recover: "odzyskaj konto" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Nie pamiętasz hasła?" + authenticate_gplus: "Autoryzuj G+" + load_profile: "Wczytaj Profil G+" + finishing: "Kończenie" + sign_in_with_facebook: "Saloguj się używając Facebooka" + sign_in_with_gplus: "Zaloguj się używając G+" + signup_switch: "Chcesz stworzyć konto?" signup: - create_account_title: "Stwórz konto, aby zapisać postępy" - description: "Poświęć chwilę na bezpłatne założenie nowego konta." email_announcements: "Otrzymuj powiadomienia na e-mail" - coppa: "Mam powyżej 13 lat lub jestem spoza USA " - coppa_why: "(dlaczego?)" creating: "Tworzenie konta..." - sign_up: "Zarejestruj" - log_in: "zaloguj się używając hasła" + sign_up: "Zarejestruj się" + log_in: "Zaloguj się używając hasła" social_signup: "lub zaloguj się używając konta Facebook lub G+:" required: "Musisz się zalogować zanim przejdziesz dalej." + login_switch: "Masz już konto?" recover: recover_account_title: "Odzyskaj konto" send_password: "Wyślij hasło tymczasowe" -# recovery_sent: "Recovery email sent." + recovery_sent: "Email do dozyskania hasła został wysłany." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Główne" + secondary: "Drugorzędne" + armor: "Zbroja" + accessories: "Akcesoria" + misc: "Różne" + books: "Książki" common: + back: "Wstecz" # When used as an action verb, like "Navigate backward" + continue: "Dalej" # When used as an action verb, like "Continue forward" loading: "Ładowanie..." saving: "Zapisywanie..." sending: "Wysyłanie…" -# send: "Send" + send: "Wyślij" cancel: "Anuluj" save: "Zapisz" -# publish: "Publish" -# create: "Create" + publish: "Opublikuj" + create: "Stwórz" manual: "Ręcznie" fork: "Fork" - play: "Graj" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + play: "Zagraj" # When used as an action verb, like "Play next level" + retry: "Ponów" + actions: "Akcje" + info: "Informacje" + help: "Pomoc" + watch: "Obserwuj" + unwatch: "Nie obserwuj" + submit_patch: "Prześlij łatkę" + submit_changes: "Prześlij zmiany" + save_changes: "Zapisz zmiany" general: and: "i" - name: "Imię" -# date: "Date" + name: "Nazwa" + date: "Data" body: "Zawartość" version: "Wersja" + pending: "W trakcie" + accepted: "Przyjęto" + rejected: "Odrzucono" + withdrawn: "Wycofano" + submitter: "Przesyłający" + submitted: "Przesłano" commit_msg: "Wiadomość do commitu" -# version_history: "Version History" + review: "Recenzja" + version_history: "Historia wersji" version_history_for: "Historia wersji dla: " + select_changes: "Zaznacz dwie zmiany poniżej aby zobaczyć różnice." + undo_prefix: "Cofnij" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Ponów" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Odtwórz podgląd obecnego poziomu" result: "Wynik" results: "Wyniki" description: "Opis" @@ -172,98 +191,109 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish easy: "Łatwy" medium: "Średni" hard: "Trudny" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player: "Gracz" + player_level: "Poziom" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Wojownik" +# ranger: "Ranger" + wizard: "Czarodziej" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "sekunda" + seconds: "sekund" + minute: "minuta" + minutes: "minut" + hour: "godzina" + hours: "godzin" + day: "dzień" + days: "dni" + week: "tydzień" + weeks: "tygodni" + month: "miesiąc" + months: "miesięcy" + year: "rok" + years: "lat" play_level: done: "Zrobione" home: "Strona główna" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" + level: "Poziom" # Like "Level: Dungeons of Kithgard" + skip: "Pomiń" game_menu: "Menu gry" guide: "Przewodnik" restart: "Zacznij od nowa" goals: "Cele" -# goal: "Goal" -# running: "Running..." + goal: "Cel" + running: "Symulowanie..." success: "Sukces!" incomplete: "Niekompletne" timed_out: "Czas minął" failing: "Niepowodzenie" action_timeline: "Oś czasu" click_to_select: "Kliknij jednostkę, by ją zaznaczyć." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "Multiplayer" + control_bar_join_game: "Dołącz do gry" + reload: "Wczytaj ponownie" reload_title: "Przywrócić cały kod?" reload_really: "Czy jesteś pewien, że chcesz przywrócić kod startowy tego poziomu?" reload_confirm: "Przywróć cały kod" + victory: "Zwycięstwo" victory_title_prefix: "" victory_title_suffix: " ukończony" - victory_sign_up: "Zapisz się, by zapisać postępy" - victory_sign_up_poke: "Chcesz zapisać swój kod? Utwórz bezpłatne konto!" + victory_sign_up: "Zarejestruj się, by zapisać postępy" + victory_sign_up_poke: "Chcesz zapisać swój kod? Stwórz bezpłatne konto!" victory_rate_the_level: "Oceń poziom: " # Only in old-style levels. victory_return_to_ladder: "Powrót do drabinki" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Przejdź na następny poziom" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "Dalej" + victory_saving_progress: "Zapisywanie postępów" victory_go_home: "Powrót do strony głównej" # Only in old-style levels. victory_review: "Powiedz nam coś więcej!" # Only in old-style levels. victory_hour_of_code_done: "Skończyłeś już?" victory_hour_of_code_done_yes: "Tak, skończyłem moją Godzinę Kodu." + victory_experience_gained: "Doświadczenie zdobyte" + victory_gems_gained: "Klejnoty zdobyte" + victory_new_item: "Nowy przedmiot" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." + victory_become_a_viking: "Zostań wikingiem" guide_title: "Przewodnik" tome_minion_spells: "Czary twojego podopiecznego" # Only in old-style levels. tome_read_only_spells: "Czary tylko do odczytu" # Only in old-style levels. tome_other_units: "Inne jednostki" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" + tome_cast_button_run: "Uruchom" + tome_cast_button_running: "Uruchomiono" + tome_cast_button_ran: "Uruchomiono" + tome_submit_button: "Prześlij" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Wybierz kogoś do " tome_available_spells: "Dostępne czary" -# tome_your_skills: "Your Skills" + tome_your_skills: "Twoje umiejętności" + tome_help: "Pomoc" # tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + hud_continue_short: "Kontynuuj" + code_saved: "Kod zapisano" skip_tutorial: "Pomiń (esc)" keyboard_shortcuts: "Skróty klawiszowe" loading_ready: "Gotowy!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" + loading_start: "Rozpocznij poziom" + problem_alert_title: "Popraw swój kod" + problem_alert_help: "Pomoc" time_current: "Teraz:" -# time_total: "Max:" + time_total: "Maksymalnie:" time_goto: "Idź do:" + non_user_code_problem_title: "Błąd podczas ładowania poziomu" + infinite_loop_title: "Wykryto niekończącą się pętlę" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." + check_dev_console_link: "(instrukcje)" infinite_loop_try_again: "Próbuj ponownie" infinite_loop_reset_level: "Resetuj poziom" infinite_loop_comment_out: "Comment Out My Code" tip_toggle_play: "Włącz/zatrzymaj grę naciskając Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ i Ctrl+] przesuwają czas." + tip_scrub_shortcut: "Ctrl+[ i Ctrl+] przesuwają czas." # {change} tip_guide_exists: "Klikając Przewodnik u góry strony uzyskasz przydatne informacje." tip_open_source: "CodeCombat ma w 100% otwarty kod!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat uruchomił fazę beta w październiku 2013." tip_think_solution: "Myśl nad rozwiązaniem, nie problemem." tip_theory_practice: "W teorii nie ma różnicy między teorią a praktyką. W praktyce jednak, jest. - Yogi Berra" @@ -289,34 +319,69 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Spersonalizuj czarodzieja" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Ekwipunek" save_load_tab: "Zapisz/Wczytaj" options_tab: "Opcje" guide_tab: "Przewodnik" + guide_video_tutorial: "Wideo poradnik" + guide_tips: "Wskazówki" multiplayer_tab: "Multiplayer" -# auth_tab: "Sign Up" + auth_tab: "Zarejestruj się" inventory_caption: "Wyposaż swojego bohatera" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" + choose_hero_caption: "Wybierz bohatera, język" + save_load_caption: "... i przejrzyj historię" options_caption: "Ustaw opcje" guide_caption: "Dokumenty i wskazówki" multiplayer_caption: "Graj ze znajomymi!" -# auth_caption: "Save your progress." + auth_caption: "Zapisz swój postęp." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Najlepsze wyniki" + view_other_solutions: "Pokaż tablicę wyników" + scores: "Wyniki" + top_players: "Najlepsi gracze" + day: "Dziś" + week: "W tym tygodniu" + all: "Zawsze" + time: "Czas" + damage_taken: "Obrażenia otrzymane" + damage_dealt: "Obrażenia zadane" + difficulty: "Trudność" + gold_collected: "Złoto zebrane" + + inventory: + choose_inventory: "Załóż przedmioty" + equipped_item: "Założone" + required_purchase_title: "Wymagane" + available_item: "Dostępne" + restricted_title: "Zabronione" + should_equip: "(kliknij podwójnie, aby założyć)" + equipped: "(założone)" + locked: "(zablokowane)" + restricted: "(zabronione)" + equip: "Załóż" + unequip: "Zdejmij" # buy_gems: # few_gems: "A few gems" @@ -325,61 +390,146 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" + feature5: "Poradniki wideo" +# feature6: "Premium email support" + feature7: "Prywatne <strong>Klany</strong>" + free: "Za darmo" + month: "miesięcznie" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" + choose_hero: "Wybierz swojego bohatera" + programming_language: "Język programowania" + programming_language_description: "Którego języka programowania chcesz używać?" + default: "domyślny" + experimental: "Eksperymentalny" python_blurb: "Prosty ale potężny." javascript_blurb: "Język internetu." coffeescript_blurb: "Przyjemniejsza składnia JavaScript." clojure_blurb: "Nowoczesny Lisp." lua_blurb: "Język skryptowy gier." io_blurb: "Prosty lecz nieznany." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + status: "Status" + hero_type: "Typ" + weapons: "Bronie" + weapons_warrior: "Miecze - Krótki zasięg, Brak magii" + weapons_ranger: "Kusze, Pistolety - Daleki zasięg, Brak magii" + weapons_wizard: "Różdżki, Laski - Daleki zasięg, Magia" + attack: "Obrażenia" # Can also translate as "Attack" + health: "Życie" + speed: "Szybkość" + regeneration: "Regenaracja" + range: "Zasięg" # As in "attack or visual range" + blocks: "Blok" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" + skills: "Umiejętności" + attack_1: "Zadaje" +# attack_2: "of listed" +# attack_3: "weapon damage." + health_1: "Zdobywa" +# health_2: "of listed" +# health_3: "armor health." + speed_1: "Idzie do" + speed_2: "metrów na sekundę." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Musisz odblokować poziom:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Tylko nieliczni bohaterowie mogą brać udział w tym poziomie." -# skill_docs: + skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" + read_only: "tylko do odczytu" + action_name: "nazwa" # action_cooldown: "Takes" # action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + action_damage: "Obrażenia" + action_range: "Zasięg" + action_radius: "Promień" + action_duration: "Czas trwania" + example: "Przykład" + ex: "np." # Abbreviation of "example" + current_value: "Aktualna wartość" + default_value: "Domyślna wartość" + parameters: "Parametry" + returns: "Zwraca" + granted_by: "Zdobyte dzięki:" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + save_load: + granularity_saved_games: "Zapisano" + granularity_change_history: "Historia" options: general_options: "Opcje ogólne" # Check out the Options tab in the Game Menu while playing a level volume_label: "Głośność" music_label: "Muzyka" music_description: "Wł/Wył muzykę w tle." - autorun_label: "Autostart" - autorun_description: "Ustawia automatyczne wykonywanie kodu." editor_config: "Konfiguracja edytora" editor_config_title: "Konfiguracja edytora" editor_config_level_language_label: "Język dla tego poziomu" @@ -411,37 +561,131 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" + team: "Zespół" + george_title: "Współzałożyciel" # george_blurb: "Businesser" -# scott_title: "Programmer" + scott_title: "Współzałożyciel" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" + nick_title: "Współzałożyciel" # nick_blurb: "Motivation Guru" -# michael_title: "Programmer" + michael_title: "Programista" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + matt_title: "Programista" # {change} + matt_blurb: "Rowerzysta" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" + josh_blurb: "Podłoga to lawa" + jose_title: "Muzyka" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Zapisz nową wersję" new_major_version: "Nowa wersja główna" + submitting_patch: "Przesyłanie łatki..." cla_prefix: "Aby zapisać zmiany, musisz najpierw zaakceptować naszą" cla_url: "umowę licencyjną dla współtwórców (CLA)" cla_suffix: "." cla_agree: "AKCEPTUJĘ" + owner_approve: "Przed pojawieniem się zmian, właściciel musi je zatwierdzić." contact: contact_us: "Kontakt z CodeCombat" welcome: "Miło Cię widzieć! Użyj tego formularza, żeby wysłać do nas wiadomość. " - contribute_prefix: "Jeśli jesteś zainteresowany wsparciem projektu, zapoznaj się z naszą " - contribute_page: "stroną współpracy" - contribute_suffix: "!" forum_prefix: "W sprawach ogólnych, skorzystaj z " forum_page: "naszego forum" forum_suffix: "." + faq_prefix: "Jest też" + faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." + where_reply: "Gdzie mamy odpisać?" send: "Wyślij wiadomość" -# contact_candidate: "Contact Candidate" # Deprecated + contact_candidate: "Kontakt z kandydatem" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated account_settings: @@ -450,12 +694,19 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish autosave: "Zmiany zapisują się automatycznie" me_tab: "Ja" picture_tab: "Zdjęcie" + delete_account_tab: "Usuń swoje konto" + wrong_email: "Błędny e-mail" + wrong_password: "Błędne zdjęcie" upload_picture: "Wgraj zdjęcie" + delete_this_account: "Usuń to konto całkowicie" +# god_mode: "God Mode" password_tab: "Hasło" emails_tab: "Powiadomienia" admin: "Administrator" new_password: "Nowe hasło" new_password_verify: "Zweryfikuj" + type_in_email: "Wpisz swój email, aby potwierdzić usunięcie konta." + type_in_password: "Wpisz również swoje hasło." email_subscriptions: "Powiadomienia email" email_subscriptions_none: "Brak powiadomień e-mail." email_announcements: "Ogłoszenia" @@ -481,16 +732,15 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish job_profile_explanation: "Witaj! Wypełnij to, a będziemy w kontakcie w sprawie znalezienie dla Ciebe pracy twórcy oprogramowania." sample_profile: "Zobacz przykładowy profil" view_profile: "Zobacz swój profil" - wizard_tab: "Czarodziej" - wizard_color: "Kolor ubrań czarodzieja" keyboard_shortcuts: keyboard_shortcuts: "Skróty klawiszowe" space: "Spacja" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# run_code: "Run current code." + enter: "Enter" + press_enter: "naciśnij enter" + escape: "Escape" + shift: "Shift" + run_code: "Uruchom obecny kod." run_real_time: "Uruchom \"na żywo\"." continue_script: "Kontynuuj ostatni skrypt." # skip_scripts: "Skip past all skippable scripts." @@ -503,10 +753,9 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." -# community: -# main_title: "CodeCombat Community" + community: + main_title: "Społeczność CodeCombat" # introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" # level_editor_prefix: "Use the CodeCombat" # level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" @@ -514,61 +763,126 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." # article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" # article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" + find_us: "Znajdź nas na tych stronach" # social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" + social_discource: "Dołącz do dyskusji na naszym forum" + social_facebook: "Polub CodeCombat na Facebooku" + social_twitter: "Obserwuj CodeCombat na Twitterze" + social_gplus: "Dołącz do CodeCombat na Google+" # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" + clans: + clan: "Klan" + clans: "Klany" + new_name: "Nazwa nowego klanu" + new_description: "Opis nowego klanu" + make_private: "Stwórz prywatny klan" + subs_only: "tylko subskrybenci" + create_clan: "Stwórz nowy klan" + private_preview: "Podgląd" + public_clans: "Publiczne klany" + my_clans: "Moje klany" + clan_name: "Nazwa klanu" + name: "Nazwa" + chieftain: "Dowódca" + type: "Typ" + edit_clan_name: "Edytuj nazwe klanu" + edit_clan_description: "Edytuj opis klanu" + edit_name: "edytuj nazwe" + edit_description: "edytuj opis" + private: "(prywatny)" + summary: "Podsumowanie" + average_level: "Średni poziom" + average_achievements: "Średnio osiągnięć" + delete_clan: "Usuń klan" + leave_clan: "Opuść klan" + join_clan: "Dołacz do klanu" + invite_1: "Zaproszenie:" + invite_2: "*Zaproś nowe osoby do klanu wysyłając im ten link." + members: "Członkowie" + progress: "Postęp" + not_started_1: "nierozpoczęty" + started_1: "rozpoczęty" + complete_1: "ukończony" +# exp_levels: "Expand levels" + rem_hero: "Usuń bohatera" + status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" + playtime: "Czas gyr" + last_played: "Ostatnio grany" + classes: archmage_title: "Arcymag" archmage_title_description: "(programista)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "Rzemieślnik" artisan_title_description: "(twórca poziomów)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "Podróżnik" adventurer_title_description: "(playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Skryba" scribe_title_description: "(twórca artykułów)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "Dyplomata" diplomat_title_description: "(tłumacz)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "Ambasador" ambassador_title_description: "(wsparcie)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: main_title: "Edytory CodeCombat" article_title: "Edytor artykułów" thang_title: "Edytor obiektów" level_title: "Edytor poziomów" -# achievement_title: "Achievement Editor" -# back: "Back" + achievement_title: "Edytor osiągnięć" + poll_title: "Edytor ankiet" + back: "Wstecz" revert: "Przywróć" revert_models: "Przywróć wersję" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" + pick_a_terrain: "Wybierz teren" + dungeon: "Loch" + indoor: "Wnętrze" + desert: "Pustynia" + grassy: "Trawa" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Mały" + large: "Duży" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" -# more: "More" + more: "Więcej" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" level_some_options: "Trochę opcji?" level_tab_thangs: "Obiekty" level_tab_scripts: "Skrypty" level_tab_settings: "Ustawienia" level_tab_components: "Komponenty" level_tab_systems: "Systemy" -# level_tab_docs: "Documentation" + level_tab_docs: "Documentacja" level_tab_thangs_title: "Aktualne obiekty" -# level_tab_thangs_all: "All" + level_tab_thangs_all: "Wszystkie" level_tab_thangs_conditions: "Warunki początkowe" level_tab_thangs_add: "Dodaj obiekty" -# delete: "Delete" -# duplicate: "Duplicate" -# rotate: "Rotate" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" + delete: "Usuń" + duplicate: "Powiel" + stop_duplicate: "Przestań powielać" + rotate: "Obróć" level_settings_title: "Ustawienia" level_component_tab_title: "Aktualne komponenty" level_component_btn_new: "Stwórz nowy komponent" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" + new_poll_title: "Stwórz nową ankietę" + new_poll_title_login: "Zaloguj się aby stworzyć nową ankietę" article_search_title: "Przeszukaj artykuły" thang_search_title: "Przeszukaj typy obiektów" level_search_title: "Przeszukaj poziomy" -# achievement_search_title: "Search Achievements" + achievement_search_title: "Szukaj osiągnięcia" + poll_search_title: "Szukaj ankiety" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" + tasks: "Zadania" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" + done_adding: "Zakończono dodawanie" article: edit_btn_preview: "Podgląd" edit_article_title: "Edytuj artykuł" + polls: + priority: "Priorytet" + contribute: page_title: "Współpraca" - character_classes_title: "Klasy postaci" - introduction_desc_intro: "Pokładamy w CodeCombat duże nadzieje." - introduction_desc_pref: "Chcemy być miejscem, w którym programiści wszelkiej maści wspólnie uczą się i bawią, zapoznają innych ze wspaniałym światem programowania i odzwierciedlają najlepsze elementy społeczności. Nie możemy, ani nie chcemy, robić tego samodzielnie; to, co czyni projekty takie jak GitHub, Stack Overflow czy Linux wielkimi to ludzie, którzy ich używają i tworzą opierając się na nich. W związku z tym, " - introduction_desc_github_url: "CodeCombat jest całkowicie open source" - introduction_desc_suf: " i zamierzamy zapewnić tak wiele sposobów na współpracę w projekcie jak to tylko możliwe, by był on tak samo nasz, jak i wasz." - introduction_desc_ending: "Liczymy, że dołączysz się do nas!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy i Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" alert_account_message_intro: "Hej tam!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "Zainteresowany pracą przy grafice, interfejsie użytkownika, organizacji bazy danych oraz serwera, łączności multiplayer, fizyce, dźwięku lub wydajności silnika gry? Chciałbyś dołączyć się do budowania gry, która pomoże innym nauczyć się umiejętności, które sam posiadasz? Mamy wiele do zrobienia i jeśli jesteś doświadczonym programistą chcącym pomóc w rozwoju CodeCombat, ta klasa jest dla ciebie. Twoja pomoc przy budowaniu najlepszej gry programistycznej w historii będzie nieoceniona." archmage_introduction: "Jedną z najlepszych rzeczy w tworzeniu gier jest to, że syntetyzują one tak wiele różnych spraw. Grafika, dźwięk, łączność w czasie rzeczywistym, social networking i oczywiście wiele innych, bardziej popularnych, aspektów programowania, od niskopoziomowego zarządzania bazami danych i administracji serwerem do interfejsu użytkownika i jego tworzenia. Jest wiele do zrobienia i jeśli jesteś doświadczonym programistą z zacięciem, by zajrzeć do sedna CodeCombat, ta klasa może być dla ciebie. Bylibyśmy niezmiernie szczęśliwi mając twoją pomoc przy budowaniu najlepszej programistycznej gry wszech czasów." class_attributes: "Atrybuty klasowe" archmage_attribute_1_pref: "Znajomość " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish join_desc_4: ", a dowiesz się wszystkiego!" join_url_email: "Napisz do nas" join_url_hipchat: "publicznego pokoju HipChat" - more_about_archmage: "Dowiedz się więcej o stawaniu się Arcymagiem" archmage_subscribe_desc: "Otrzymuj e-maile dotyczące nowych okazji programistycznych oraz ogłoszeń." - artisan_summary_pref: "Chcesz projektować poziomy i rozwijać arsenał CodeCombat? Ludzie grają w dostarczane przez nas zasoby szybciej, niż potrafimy je tworzyć! Obecnie, nasz edytor jest dosyć niemrawy więc czuj się ostrzeżony - tworzenie poziomów przy jego pomocy może być trochę wymagające i zbugowane. Jeśli masz wizję nowych kampanii, od pętli typu for do" - artisan_summary_suf: ", ta klasa jest dla ciebie." artisan_introduction_pref: "Musimy stworzyć dodatkowe poziomy! Ludzie będą oczekiwać nowych zasobów, a my mamy ograniczone możliwości co do naszych mocy przerobowych. Obecnie, twoja stacja robocza jest na poziomie pierwszym; nasz edytor poziomów jest ledwo używalny nawet przez jego twórców - bądź tego świadom. Jeśli masz wizję nowych kampanii, od pętli typu for do" artisan_introduction_suf: ", ta klasa może być dla ciebie." artisan_attribute_1: "Jakiekolwiek doświadczenie w tworzeniu zasobów tego typu byłoby przydatne, na przykład używając edytora poziomów dostarczonego przez Blizzard. Nie jest to jednak wymagane." @@ -645,47 +959,37 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish artisan_join_step2: "Stwórz nowy poziom i przejrzyj istniejące poziomy." artisan_join_step3: "Zajrzyj do naszego publicznego pokoju HipChat, aby uzyskać pomoc." artisan_join_step4: "Pokaż swoje poziomy na forum, aby uzyskać opinie." - more_about_artisan: "Dwiedz się więcej na temat stawania się Rzemieślnikiem" artisan_subscribe_desc: "Otrzymuj e-maile dotyczące aktualności w tworzeniu poziomów i ogłoszeń." - adventurer_summary: "Bądźmy szczerzy co do twojej roli: jesteś tankiem. Będziesz przyjmował ciężkie obrażenia. Potrzebujemy ludzi do testowania nowych poziomów i pomocy w rozpoznawaniu ulepszeń, które będzie można do nich zastosować. Będzie to bolesny proces; tworzenie dobrych gier to długi proces i nikt nie trafia w dziesiątkę za pierwszym razem. Jeśli jesteś wytrzymały i masz wysoki wskaźnik constitution (D&D), ta klasa jest dla ciebie." adventurer_introduction: "Bądźmy szczerzy co do twojej roli: jesteś tankiem. Będziesz przyjmował ciężkie obrażenia. Potrzebujemy ludzi do testowania nowych poziomów i pomocy w rozpoznawaniu ulepszeń, które będzie można do nich zastosować. Będzie to bolesny proces; tworzenie dobrych gier to długi proces i nikt nie trafia w dziesiątkę za pierwszym razem. Jeśli jesteś wytrzymały i masz wysoki wskaźnik constitution (D&D), ta klasa jest dla ciebie." adventurer_attribute_1: "Głód wiedzy. Chcesz nauczyć się programować, a my chcemy ci to umożliwić. Prawdopodobnie w tym przypadku, to ty będziesz jednak przez wiele czasu stroną uczącą." adventurer_attribute_2: "Charyzma. Bądź uprzejmy, ale wyraźnie określaj, co wymaga poprawy, oferując sugestie co do sposobu jej uzyskania." adventurer_join_pref: "Zapoznaj się z Rzemieślnikiem (lub rekrutuj go!), aby wspólnie pracować lub też zaznacz kratkę poniżej, aby otrzymywać e-maile, kiedy pojawią się nowe poziomy do testowania. Będziemy również pisać o poziomach do sprawdzenia na naszych stronach w sieciach społecznościowych jak" adventurer_forum_url: "nasze forum" adventurer_join_suf: "więc jeśli wolałbyś być informowany w ten sposób, zarejestruj się na nich!" - more_about_adventurer: "Dowiedz się więcej o stawaniu się Podróżnikiem" adventurer_subscribe_desc: "Otrzymuj e-maile, gdy pojawią się nowe poziomy do tesotwania." - scribe_summary_pref: "Codecombat nie będzie tylko zbieraniną poziomów. Będzie też źródłem wiedzy programistycznej, na której gracze będą mogli sie opierać. Dzięki temu, każdy z Rzemieślników będzie mógł podać link do szczegółowego artykułu, który pomoże graczowi: dokumentacji w stylu " - scribe_summary_suf: ". Jeśli lubisz wyjaśniać idee programistyczne, ta klasa jest dla ciebie." scribe_introduction_pref: "CodeCombat nie będzie tylko zbieraniną poziomów. Będzie też zawierać źródło wiedzy, wiki programistycznych idei, na której będzie można oprzeć poziomy. Dzięki temu, każdy z Rzemieślników zamiast opisywać ze szczegółami, czym jest operator porónania, będzie mógł po prostu podać graczowi w swoim poziomie link do artykułu opisującego go. Mamy na myśli coś podobnego do " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: ". Jeśli twoją definicją zabawy jest artykułowanie idei programistycznych przy pomocy składni Markdown, ta klasa może być dla ciebie." scribe_attribute_1: "Umiejętne posługiwanie się słowem to właściwie wszystko, czego potrzebujesz. Nie tylko gramatyka i ortografia, ale również umiejętnośc tłumaczenia trudnego materiału innym." contact_us_url: "Skontaktuj się z nami" scribe_join_description: "powiedz nam coś o sobie, swoim doświadczeniu w programowaniu i rzeczach, o których chciałbyś pisać, a chętnie to z tobą uzgodnimy!" - more_about_scribe: "Dowiedz się więcej o stawaniu się Skrybą" scribe_subscribe_desc: "Otrzymuj e-maile na temat ogłoszeń dotyczących pisania artykułów." - diplomat_summary: "W krajach nieanglojęzycznych istnieje wielkie zainteresowanie CodeCombat! Szukamy tłumaczy chętnych do poświęcenia swojego czasu na tłumaczenie treści strony, aby CodeCombat było dostępne dla całego świata tak szybko, jak to tylko możliwe. Jeśli chcesz pomóc w sprawieniu, by CodeCombat było prawdziwie międzynarodowe, ta klasa jest dla ciebie." diplomat_introduction_pref: "Jeśli dowiedzieliśmy jednej rzeczy z naszego " diplomat_launch_url: "otwarcia w październiku" diplomat_introduction_suf: ", to jest nią informacja o znacznym zainteresowaniu CodeCombat w innych krajach. Tworzymy zespół tłumaczy chętnych do przemieniania zestawów słów w inne zestawy słów, aby CodeCombat było tak dostępne dla całego świata, jak to tylko możliwe. Jeśli chciabyś mieć wgląd w nadchodzącą zawartość i umożliwić swoim krajanom granie w najnowsze poziomy, ta klasa może być dla ciebie." diplomat_attribute_1: "Biegła znajomość angielskiego oraz języka, na który chciałbyś tłumaczyć. Kiedy przekazujesz skomplikowane idee, dobrze mieć płynność w obu z nich!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." + diplomat_i18n_page_prefix: "Możesz zacząć tłumaczyć nasze poziomy przechodząc na naszą" + diplomat_i18n_page: "stronę tłumaczeń" + diplomat_i18n_page_suffix: ", albo nasz interfejs i stronę na GitHub." diplomat_join_pref_github: "Znajdź plik lokalizacyjny dla wybranego języka " diplomat_github_url: "na GitHubie" diplomat_join_suf_github: ", edytuj go online i wyślij pull request. Do tego, zaznacz kratkę poniżej, aby być na bieżąco z naszym międzynarodowym rozwojem!" - more_about_diplomat: "Dowiedz się więcej o stawaniu się Dyplomatą" diplomat_subscribe_desc: "Otrzymuj e-maile na temat postępów i18n i poziomów do tłumaczenia." - ambassador_summary: "Staramy się zbudować społeczność, a każda społeczność potrzebuje zespołu wsparcia, kiedy pojawią się kłopoty. Mamy czaty, e-maile i strony w sieciach społecznościowych, aby nasi użytkownicy mogli zapoznać się z grą. Jeśli chcesz pomóc ludziom w tym, jak do nas dołączyć, dobrze się bawić, a do tego poznać tajniki programowania, ta klasa jest dla ciebie." ambassador_introduction: "Oto społeczność, którą budujemy, a ty jesteś jej łącznikiem. Mamy czaty, e-maile i strony w sieciach społecznościowych oraz wielu ludzi potrzebujących pomocy w zapoznaniu się z grą oraz uczeniu się za jej pomocą. Jeśli chcesz pomóc ludziom, by do nas dołączyli i dobrze się bawili oraz mieć pełne poczucie tętna CodeCombat oraz kierunku, w którym zmierzamy, ta klasa może być dla ciebie." ambassador_attribute_1: "Umiejętność komunikacji. Musisz umieć rozpoznać problemy, które mają gracze i pomóc im je rozwiązać. Do tego, informuj resztę z nas, co mówią gracze - na co się skarżą, a czego chcą jeszcze więcej!" ambassador_join_desc: "powiedz nam coś o sobie, jakie masz doświadczenie i czym byłbyś zainteresowany. Chętnie z tobą porozmawiamy!" ambassador_join_note_strong: "Uwaga" ambassador_join_note_desc: "Jednym z naszych priorytetów jest zbudowanie trybu multiplayer, gdzie gracze mający problem z rozwiązywaniem poziomów będą mogli wezwać czarodziejów wyższego poziomu, by im pomogli. Będzie to świetna okazja dla Ambasadorów. Spodziewajcie się ogłoszenia w tej sprawie!" - more_about_ambassador: "Dowiedz się więcej o stawaniu się Ambasadorem" ambassador_subscribe_desc: "Otrzymuj e-maile dotyczące aktualizacji wsparcia oraz rozwoju trybu multiplayer." changes_auto_save: "Zmiany zapisują się automatycznie po kliknięci kratki." diligent_scribes: "Nasi pilni Skrybowie:" @@ -702,11 +1006,11 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish simulation_explanation: "Symulując gry możesz szybciej uzyskać ocenę swojej gry!" simulate_games: "Symuluj gry!" simulate_all: "RESETUJ I SYMULUJ GRY" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" + games_simulated_by: "Gry symulowane przez Ciebie:" + games_simulated_for: "Gry symulowane dla Ciebie:" + games_simulated: "Gier zasymulowanych" + games_played: "Gier rozegranych" + ratio: "Proporcje" leaderboard: "Tabela rankingowa" battle_as: "Walcz jako " summary_your: "Twój " @@ -719,8 +1023,8 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish rank_submitted: "Wysłano do oceny" rank_failed: "Błąd oceniania" rank_being_ranked: "Aktualnie oceniane gry" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" + rank_last_submitted: "przesłano " + help_simulate: "Pomóc w symulowaniu gier?" code_being_simulated: "Twój nowy kod jest aktualnie symulowany przez innych graczy w celu oceny. W miarę pojawiania sie nowych pojedynków, nastąpi odświeżenie." no_ranked_matches_pre: "Brak ocenionych pojedynków dla drużyny " no_ranked_matches_post: " ! Zagraj przeciwko kilku oponentom i wróc tutaj, aby uzyskać ocenę gry." @@ -734,45 +1038,48 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish simple_ai: "Proste AI" warmup: "Rozgrzewka" # friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" + log_in_for_friends: "Zaloguj się by grać ze swoimi znajomymi!" # social_connect_blurb: "Connect and play against your friends!" # invite_friends_to_battle: "Invite your friends to join you in battle!" # fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" + watch_victory: "Obejrzyj swoje zwycięstwo" + defeat_the: "Pokonaj" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + rules: "Zasady" + winners: "Zwycięzcy" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + user: + stats: "Statystyki" + singleplayer_title: "Poziomy jednoosobowe" + multiplayer_title: "Poziomy multiplayer" + achievements_title: "Osiągnięcia" + last_played: "Ostatnio grany" + status: "Status" + status_completed: "Ukończono" + status_unfinished: "Nie ukończono" + no_singleplayer: "Nie rozegrał żadnej gry jednoosobowej." + no_multiplayer: "Nie rozegrał żadnej gry multiplayer." + no_achievements: "Nie zdobył żadnych osiągnięć." + favorite_prefix: "Ulubiony język to " + favorite_postfix: "." + not_member_of_clans: "Nie jest członkiem żadnego klanu." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" + achievements: + last_earned: "Ostatnio zdobyty" + amount_achieved: "Ilość" + achievement: "Osiągnięcie" # category_contributor: "Contributor" # category_ladder: "Ladder" -# category_level: "Level" + category_level: "Poziom" # category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" + category_levels: "Poziomy" # category_undefined: "Uncategorized" # current_xp_prefix: "" # current_xp_postfix: " in total" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -799,10 +1135,10 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # server_error: "Server error." # unknown: "Unknown error." -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" + resources: + sessions: "Sesje" + your_sessions: "Twoje sesje" + level: "Poziom" # social_network_apis: "Social Network APIs" # facebook_status: "Facebook Status" # facebook_friends: "Facebook Friends" @@ -811,8 +1147,9 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # gplus_friend_sessions: "G+ Friend Sessions" # leaderboard: "Leaderboard" # user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" + user_profile: "Profil użytkownika" + patch: "Łatka" + patches: "Łatki" # patched_model: "Source Document" # model: "Model" # system: "System" @@ -823,36 +1160,63 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # thangs: "Thangs" # level_session: "Your Session" # opponent_session: "Opponent Session" -# article: "Article" + article: "Artykuł" # user_names: "User Names" # thang_names: "Thang Names" # files: "Files" -# top_simulators: "Top Simulators" + top_simulators: "Najlepsi symulatorzy" # source_document: "Source Document" # document: "Document" # sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" + employers: "Pracodawcy" + candidates: "Kandydaci" # candidate_sessions: "Candidate Sessions" # user_remark: "User Remark" # user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" + versions: "Wersje" + items: "Przedmioty" + hero: "Bohater" + heroes: "Bohaterowie" + achievement: "Osiągnięcie" # clas: "CLAs" # play_counts: "Play Counts" -# feedback: "Feedback" + feedback: "Wsparcie" +# payment_info: "Payment Info" + campaigns: "Kampanie" + poll: "Ankieta" + user_polls_record: "Historia oddanych głosów" -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" + concepts: +# advanced_strings: "Advanced Strings" + algorithms: "Algorytmy" + arguments: "Argumenty" + arithmetic: "Arytmetyka" + arrays: "Tablice" + basic_syntax: "Podstawy składni" + boolean_logic: "Algebra Boole'a" +# break_statements: "Break Statements" + classes: "Klasy" + for_loops: "Pętle for" + functions: "Funkcje" + if_statements: "Wyrażenia warunkowe" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" + strings: "Ciągi znaków" + variables: "Zmienne" + vectors: "Wektory" + while_loops: "Pętle" + recursion: "Rekurencja" + + delta: + added: "Dodano" + modified: "Zmieniono" +# not_modified: "Not Modified" + deleted: "Usunięto" # moved_index: "Moved Index" # text_diff: "Text Diff" # merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" + no_changes: "Brak zmian" # guide: # temp: "Temp" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish practices_title: "Ludzkim językiem" practices_description: "Oto nasze obietnice wobec ciebie, gracza, wyrażone po polsku, bez prawniczego żargonu." privacy_title: "Prywatność" - privacy_description: "Nie będziemy sprzedawać żadnych z twoich prywatnych informacji. Planujemy w pewnym momencie zarobić trochę pieniędzy dzięki rekrutacjom, ale możesz spać spokojnie - nie będziemy rozpowszechniać twoich osobistych informacji zainteresowanym firmom bez twojej jednoznacznej zgody." + privacy_description: "Nie sprzedamy żadnej z Twoich prywatnych informacji." security_title: "Bezpieczeństwo" security_description: "Z całych sił staramy się zabezpieczyć twoje prywatne informacje. Jako że jesteśmy projektem open source, każdy może sprawdzić i ulepszyć nasz system zabezpieczeń." email_title: "E-mail" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish email_description_suffix: "lub poprzez linki w e-mailach, które wysyłamy, możesz zmienić swoje ustawienia i w prosty sposób wypisać się z subskrypcji w dowolnym momencie." cost_title: "Koszty" cost_description: "W tym momencie CodeCombat jest w stu procentach darmowe! Jednym z naszych głównych celów jest, by utrzymać taki stan rzeczy, aby jak najwięcej ludzi miało dostęp do gry, bez względu na ich zasobność. Jeśli nadejdą gorsze dni, dopuszczamy możliwość wprowadzenia płatnych subskrypcji lub pobierania opłat za część zawartości, ale wolelibyśmy, by tak się nie stało. Przy odrobinie szczęścia, uda nam się podtrzymać obecną sytuację dzięki:" - recruitment_title: "Rekrutacji" - recruitment_description_prefix: "Dzięki CodeCombat, staniesz się potężnym czarodziejem - nie tylko w grze, ale również w prawdziwym życiu." - url_hire_programmers: "Firmy nie nadążają z zatrudnianiem programistów" - recruitment_description_suffix: "więc kiedy tylko odpowiednio się wyszkolisz i wyrazisz zgodę, zaprezentujemy twoje programistyczne dokonania tysiącom pracodawców tylko czekających, by dać ci pracę. Oni płacą nam trochę, tobie" - recruitment_description_italic: "dużo" - recruitment_description_ending: "strona pozostaje darmowa i wszyscy są szczęśliwi. Tak wygląda plan." copyrights_title: "Prawa autorskie i licencje" contributor_title: "Umowa licencyjna dla współtwórców (CLA)" contributor_description_prefix: "Wszyscy współtwórcy, zarówno ci ze strony jak i ci z GitHuba, podlegają naszemu" @@ -900,11 +1258,11 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish contributor_description_suffix: ", na które powinieneś wyrazić zgodę przed dodaniem swojego wkładu." code_title: "Kod - MIT" code_description_prefix: "Całość kodu posiadanego przez CodeCombat lub hostowanego na codecombat.com, zarówno w repozytorium GitHub, jak i bazie codecombat.com, podlega licencji" -# mit_license_url: "MIT license" + mit_license_url: "Licencja MIT" code_description_suffix: "Zawiera się w tym całość kodu w systemach i komponentach, które są udostępnione przez CodeCombat w celu tworzenia poziomów." art_title: "Grafika/muzyka - Creative Commons " art_description_prefix: "Całość ogólnej treści dostępna jest pod licencją" -# cc_license_url: "Creative Commons Attribution 4.0 International License" + cc_license_url: "Międzynarodowa Licencja Creative Commons Attribution 4.0" art_description_suffix: "Zawartość ogólna to wszystko, co zostało publicznie udostępnione przez CodeCombat w celu tworzenia poziomów. Wchodzą w to:" art_music: "Muzyka" art_sound: "Dźwięki" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Ustawienia czarodzieja" - customize_avatar: "Personalizuj swój awatar" - active: "Aktywuj" - color: "Kolor" - group: "Rodzaj" - clothes: "Ubrania" - trim: "Dodatki" - cloud: "Chmura" - team: "Drużyna" - spell: "Zaklęcie" - boots: "Buty" - hue: "Odcień" - saturation: "Nasycenie" - lightness: "Jasność" - account_profile: settings: "Ustawienia" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Edytuj Profil" diff --git a/app/locale/pt-BR.coffee b/app/locale/pt-BR.coffee index 62db838e7..3d4a5f366 100644 --- a/app/locale/pt-BR.coffee +++ b/app/locale/pt-BR.coffee @@ -1,16 +1,17 @@ -module.exports = nativeDescription: "português do Brasil", englishDescription: "Portuguese (Brazil)", translation: +module.exports = nativeDescription: "Português do Brasil", englishDescription: "Portuguese (Brazil)", translation: home: - slogan: "Aprenda a programar enquanto se diverte com um jogo." + slogan: "Aprenda a programar enquanto se diverte jogando." no_ie: "CodeCombat não roda em versões mais antigas que o Internet Explorer 10. Desculpe!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat não foi projetado para dispositivos móveis e pode não funcionar!" # Warning that shows up on mobile devices - play: "Jogar" # The big play button that just starts playing a level + play: "Jogar" # The big play button that opens up the campaign view. old_browser: "Ops, seu navegador é muito antigo para rodar o CodeCombat. Desculpe!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Você pode tentar de qualquer forma, mas provavelmente não irá funcionar." + ipad_browser: "Más notícias:CodeCombat não é executado no navegador do iPad. Boas notícias: Nosso app nativo do iPad esta esperando a aprovação da Apple." campaign: "Campanha" for_beginners: "Para Iniciantes" multiplayer: "Multijogador" # Not currently shown on home page for_developers: "Para Desenvolvedores" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Ou baixe para iPad" nav: play: "Jogar" # The top nav bar entry where players choose which levels to play @@ -50,68 +51,66 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: play_as: "Jogar Como " # Ladder page spectate: "Assistir" # Ladder page players: "jogadores" # Hover over a level on /play - hours_played: "horas jogadas" # Hover over a level on /play - items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details + hours_played: "Horas jogadas" # Hover over a level on /play + items: "Itens" # Tooltip on item shop button from /play + unlock: "Desbloquear" # For purchasing items and heroes + confirm: "Confirmar" + owned: "Possui" # For items you own + locked: "Bloqueado" + purchasable: "Comprável" # For a hero you unlocked but haven't purchased + available: "Disponível" + skills_granted: "Habilidades Concedidas" # Property documentation details heroes: "Heróis" # Tooltip on hero shop button from /play achievements: "Conquistas" # Tooltip on achievement list button from /play account: "Conta" # Tooltip on account button from /play settings: "Configurações" # Tooltip on settings button from /play + poll: "Enquete" # Tooltip on poll button from /play next: "Próximo" # Go from choose hero to choose inventory before playing a level change_hero: "Alterar Herói" # Go back from choose inventory to choose hero - choose_inventory: "Equipar Items" -# buy_gems: "Buy Gems" - older_campaigns: "Campanhas antigas" + choose_inventory: "Equipar Itens" + buy_gems: "Comprar Gems" + subscription_required: "Requer assinatura" anonymous: "Jogador Anônimo" level_difficulty: "Dificuldade: " campaign_beginner: "Campanha Iniciante" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Escolha seu estágio" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Você pode ir para qualquer um dos estágios abaixo, ou discutir sobre eles no " - adventurer_forum: "Fórum do Aventureiro" - adventurer_suffix: "." - campaign_old_beginner: "Campanha antiga para Iniciantes" - campaign_old_beginner_description: "... na qual você aprenderá a magia da programação." - campaign_dev: "Fases Difíceis Aleatórias" - campaign_dev_description: "... nas quais você aprenderá a interface enquanto faz algo um pouco mais difícil." + awaiting_levels_adventurer_prefix: "Nós liberamos cinco níveis por semana." # {change} + awaiting_levels_adventurer: "Cadastre-se como um aventureiro" + awaiting_levels_adventurer_suffix: "para ser o primeiro a jogar as novas fases." + adjust_volume: "Ajuste o volume" campaign_multiplayer: "Arenas Multijogador" campaign_multiplayer_description: "... nas quais você programará cara-a-cara contra outros jogadores." - campaign_player_created: "Criados por Jogadores" - campaign_player_created_description: "... nos quais você batalhará contra a criatividade dos seus companheiros <a href=\"/contribute#artisan\">feiticeiros Artesãos</a>." - campaign_classic_algorithms: "Algoritmos Clássicos" - campaign_classic_algorithms_description: "...onde você aprende os algoritmos mais conhecidos em Ciência da Computação." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Você está fazendo um grande progresso! Diga a alguém o quão você aprendeu com o CodeCombat." # {change} + email_invalid: "Endereço de email inválido." + form_blurb: "Informe o e-mail deles abaixo e mostraremos a eles!" + form_label: "Endereço de Email" + placeholder: "endereço de email" + title: "Excelente Trabalho, Aprendiz" login: sign_up: "Criar conta" log_in: "Entrar" logging_in: "Entrando" log_out: "Sair" - recover: "Recuperar sua conta" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Esqueceu sua senha?" + authenticate_gplus: "Autenticar com G+" + load_profile: "Carregar Perfil do G+" + finishing: "Terminando" + sign_in_with_facebook: "Conectar com Facebook" + sign_in_with_gplus: "Conectar com G+" + signup_switch: "Deseja Criar uma Conta?" signup: - create_account_title: "Criar conta para salvar progresso" - description: "É grátis. Precisamos apenas de umas coisinhas e você estará pronto para seguir:" email_announcements: "Receber notícias por email." - coppa: "acima de 13 anos ou não estadunidense" - coppa_why: "(Por quê?)" creating: "Criando a nova conta..." sign_up: "Criar conta" log_in: "Entre com a senha" social_signup: "Ou, você pode fazer login pelo Facebook ou G+:" required: "Você precisa fazer login antes de ir por esse caminho." + login_switch: "Já possui uma conta?" recover: recover_account_title: "Recuperar conta" @@ -119,14 +118,16 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: recovery_sent: "Email de recuperação enviado." items: -# primary: "Primary" -# secondary: "Secondary" + primary: "Primário" + secondary: "Secundário" armor: "Armadura" accessories: "Accessórios" misc: "Diversos" -# books: "Books" + books: "Livros" common: + back: "Voltar" # When used as an action verb, like "Navigate backward" + continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Carregando..." saving: "Salvando..." sending: "Enviando..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: fork: "Fork" play: "Jogar" # When used as an action verb, like "Play next level" retry: "Tente novamente" + actions: "Ações" + info: "Info" + help: "Ajuda" watch: "Observar" unwatch: "Não Observar" submit_patch: "Enviar arranjo" + submit_changes: "Enviar mudanças" + save_changes: "Salvar mudanças" general: and: "e" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: date: "Data" body: "Principal" version: "Versão" - commit_msg: "Mensagem do Commit" - version_history: "Version History Histórico de Versão" + pending: "Pendente" + accepted: "Aceito" + rejected: "Rejeitado" + withdrawn: "Retirado" + submitter: "Enviar" + submitted: "Enviado" + commit_msg: "Mensagem de Submissão" + review: "Revisão" + version_history: "Histórico de Versão" version_history_for: "Histórico de Versão para: " + select_changes: "Selecione duas alterações para ver as diferenças." + undo_prefix: "Desfazer" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Refazer" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Executar prévia do nível atual" result: "Resultado" results: "Resultados" description: "Descrição" @@ -161,7 +180,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: password: "Senha" message: "Mensagem" code: "Código" - ladder: "Ladder" + ladder: "Progressão" when: "Quando" opponent: "Oponente" rank: "Classificação" @@ -173,7 +192,10 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: medium: "Médio" hard: "Difícil" player: "Jogador" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Nível" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Guerreiro" + ranger: "Arqueiro" + wizard: "Feiticeiro" units: second: "segundo" @@ -194,44 +216,44 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: play_level: done: "Pronto" home: "Início" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" + level: "Fase" # Like "Level: Dungeons of Kithgard" skip: "Pular" game_menu: "Menu do Jogo" guide: "Guia" restart: "Reiniciar" goals: "Objetivos" goal: "Objetivo" -# running: "Running..." + running: "Rodando..." success: "Sucesso!" incomplete: "Incompleto" timed_out: "Tempo esgotado" failing: "Falta" action_timeline: "Linha do Tempo das Ações" click_to_select: "Clique em um personagem para selecioná-lo." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "Multijogador" + control_bar_join_game: "Juntar-se a uma partida" + reload: "Recarregar" reload_title: "Recarregar Todo o Código?" reload_really: "Você tem certeza que quer reiniciar o estágio?" reload_confirm: "Recarregar Tudo" - victory_title_prefix: "" + victory: "Vitória" + victory_title_prefix: " Vitória " victory_title_suffix: " Completado!" victory_sign_up: "Assine para atualizações" victory_sign_up_poke: "Quer receber as últimas novidades por email? Crie uma conta grátis e nós o manteremos informado!" victory_rate_the_level: "Avalie o estágio: " # Only in old-style levels. - victory_return_to_ladder: "Retornar para a Ladder" + victory_return_to_ladder: "Retornar para a progressão" victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Jogar o próximo estágio" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_saving_progress: "Salvando Progresso" victory_go_home: "Ir à página inicial" # Only in old-style levels. victory_review: "Diga-nos mais!" # Only in old-style levels. victory_hour_of_code_done: "Terminou?" victory_hour_of_code_done_yes: "Sim, eu terminei minha Hora da Programação!" + victory_experience_gained: "XP ganho" + victory_gems_gained: "Gems ganhas" + victory_new_item: "Novo item" + victory_viking_code_school: "Pelas barbas do profeta, esse foi um nível difícil! Se você ainda não é um desenvolvedor de software, você deveria ser. Você acaba de ser priorizado para aceitação na Viking Code School, onde você pode aprender mais e se tornar um desenvolvedor web profissional em 14 semanas." + victory_become_a_viking: "Torne-se um viking" guide_title: "Guia" tome_minion_spells: "Magias dos seus subordinados" # Only in old-style levels. tome_read_only_spells: "Magias não editáveis" # Only in old-style levels. @@ -242,28 +264,36 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: tome_submit_button: "Enviar" tome_reload_method: "Recarregar o código original para este método" # Title text for individual method reload button. tome_select_method: "Selecione um Método" - tome_see_all_methods: "Visualizar todos os métodos que você pode editar" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "Visualizar todos os métodos que você pode editar" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Selecione alguém para " tome_available_spells: "Feitiços Disponíveis" tome_your_skills: "Suas habilidades" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_help: "Ajuda" + tome_current_method: "Método Atual" + hud_continue_short: "Continuar" + code_saved: "Código Salvo" skip_tutorial: "Pular (esc)" keyboard_shortcuts: "Teclas de atalho" loading_ready: "Pronto!" loading_start: "Iniciar fase" -# problem_alert_title: "Fix Your Code" + problem_alert_title: "Altere seu Código" + problem_alert_help: "Ajuda" time_current: "Agora:" time_total: "Máximo:" time_goto: "Ir para:" + non_user_code_problem_title: "Erro ao carregar o nível" + infinite_loop_title: "Loop Infinito Detectado" + infinite_loop_description: "O código inicial para construir o mundo nunca parou de rodar. Talvez seja muito lento ou tenha um loop infinito. Ou talvez tenha um bug. Você pode tentar rodar este código novamente ou resetá-lo para o estado inicial. Se isto não consertá-lo, avise-nos por favor." + check_dev_console: "Você também pode abrir o terminal do desenvolvedor para ver o que pode estar dando errado." + check_dev_console_link: "(instruções)" infinite_loop_try_again: "Tentar novamente" infinite_loop_reset_level: "Resetar nível" infinite_loop_comment_out: "Comentar Meu Código" tip_toggle_play: "Alterne entre rodando/pausado com Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ e Ctrl+] rebobina e avança." + tip_scrub_shortcut: "Ctrl+[ e Ctrl+] rebobina e avança." # {change} tip_guide_exists: "Clique no guia no topo da página para informações úteis." tip_open_source: "CodeCombat é 100% código aberto!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat lançou sua versão beta em outubro de 2013." tip_think_solution: "Pense na solução, não no problema." tip_theory_practice: "Na teoria, não existe diferença entre teoria e prática. Mas, na prática, há. - Yogi Berra" @@ -289,49 +319,158 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: tip_hofstadters_law: "Lei de Hofstadter: Sempre demora mais do que você espera, mesmo quando você leva em consideração a Lei de Hofstadter." tip_premature_optimization: "Uma otimização permatura é a raíz de todos os males. - Donald Knuth" tip_brute_force: "Na dúvida, utilize força bruta. - Ken Thompson" - customize_wizard: "Personalize o feiticeiro" + tip_extrapolation: "Existem dois tipos de pessoa: aqueles que podem extrapolar apartir de dados incompletos..." + tip_superpower: "Programar é a coisa mais próxima de termos super poderes." + tip_control_destiny: "No verdadeiro código aberto, você tem o direito de controlar seu próprio destino. - Linus Torvalds" + tip_no_code: "Nenhum código é mais rápido que código nenhum. - Kevlin Henney" + tip_code_never_lies: "Código nunca mente, comentários as vezes. — Ron Jeffries" + tip_reusable_software: "Antes do software ser reutilizável, ele primeiro tem que ser utilizável." + tip_optimization_operator: "Cada linguagem tem um operador de otimização. Na maioria delas esse operador é ‘//’" + tip_lines_of_code: "Medir o progresso de programação em linhas de código é como medir a construção de aeronaves pelo peso. — Bill Gates" + tip_source_code: "Eu gostaria de mudar o Mundo, mas eles não me deram o código fonte." + tip_javascript_java: "Java é para o JavaScript o que um Carro é para um Carpete. - Chris Heilmann" + tip_move_forward: "O que quer que você faça, continue indo em frente. - Martin Luther King Jr." + tip_google: "Tem um problema que você não pode solucionar? Google!" + tip_adding_evil: "Adicionando uma pitada de maldade." + tip_hate_computers: "As pessoas realmente pensam porque odeiam computadores. O que eles realmente odeiam são programadores ruins. - Larry Niven" + tip_open_source_contribute: "Você pode ajudar CodeCombat a melhorar!" + tip_recurse: "Para iterar é humano, para recursão, é divino. - L. Peter Deutsch" + tip_free_your_mind: "Você tem que deixar isso tudo passar, Neo. O medo, a dúvida e a descrença. Liberte sua mente - Morpheus" + tip_strong_opponents: "Mesmo o mais forte dos adversários tem sua fraqueza. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventário" save_load_tab: "Salvar/Carregar" options_tab: "Opções" guide_tab: "Guia" + guide_video_tutorial: "Vídeo Tutorial" + guide_tips: "Dicas" multiplayer_tab: "Multijogador" -# auth_tab: "Sign Up" + auth_tab: "Registrar" inventory_caption: "Equipar seu herói" choose_hero_caption: "Escolha seu herói, linguagem" save_load_caption: "... e visualizar o histórico" options_caption: "Configurar preferências" guide_caption: "Documentos e dicas" multiplayer_caption: "Jogue com seus amigos!" -# auth_caption: "Save your progress." + auth_caption: "Salve seu progresso." + + leaderboard: +# leaderboard: "Leaderboard" + view_other_solutions: "Ver Outras Soluções" # {change} + scores: "Pontuação" + top_players: "Top Jogadores por" + day: "Hoje" + week: "Essa Semana" + all: "Todo Tempo" + time: "Tempo" + damage_taken: "Dano Recebido" + damage_dealt: "Dano Causado" + difficulty: "Dificuldade" + gold_collected: "Ouro Coletado" inventory: choose_inventory: "Equipar itens" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + equipped_item: "Equipado" + required_purchase_title: "Obrigatório" + available_item: "Disponível" + restricted_title: "Restrito" + should_equip: "(dois cliques para equipar)" + equipped: "(equipado)" + locked: "(trancado)" + restricted: "(restrito nesse nível)" + equip: "Equipar" + unequip: "Desequipar" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + buy_gems: + few_gems: "Algumas gemas" + pile_gems: "Pilha de gemas" + chest_gems: "Baú de gemas" + purchasing: "Comprando..." + declined: "Seu cartão foi rejeitado, desculpe." + retrying: "Erro de servidor, tentando novamente." + prompt_title: "Gemas insulficientes" + prompt_body: "Você quer conseguir mais gemas?" + prompt_button: "Entrar na loja" + recovered: "Gemas de compras anteriores Recuperadas. Por favor atualize a pagina." + price: "x3500 / mês" + + subscribe: + comparison_blurb: "Afine suas habilidades com uma assinatura CodeCombat!" + feature1: "Mais de 60 níveis básicos entre 4 mundos" # {change} + feature2: "7 poderosos <strong>novos heróis</strong> com habilidades únicas!" # {change} + feature3: "Mais de 30 níveis bônus" # {change} + feature4: "<strong>3500 gemas bônus</strong> todo mês!" + feature5: "Vídeo tutorials" + feature6: "Suporte via e-mail Premium" + feature7: "<strong>Clãs</strong> Privados" + free: "Grátis" + month: "mês" + subscribe_title: "Inscrever-se" + unsubscribe: "Desinscrever-se" + confirm_unsubscribe: "Confirmar Desinscrição" + never_mind: "Nunca esqueça, Eu continuo amando você" + thank_you_months_prefix: "Obrigado por nos apoiar" + thank_you_months_suffix: "meses." + thank_you: "Obrigado por estar apoiando o CodeCombat." + sorry_to_see_you_go: "É uma pena ver você indo embora! Por favor, diga o que podemos fazer para melhorar." + unsubscribe_feedback_placeholder: "Oh, o que nós fizemos?" + parent_button: "Pergunte aos seus pais" + parent_email_description: "Nós enviaremos um e-mail para eles adquirir para você uma assinatura do CodeCombat." + parent_email_input_invalid: "Endereço de e-mail inválido." + parent_email_input_label: "Endereço de e-mail dos pais" + parent_email_input_placeholder: "Informe o e-mail dos pais" + parent_email_send: "Enviar Email" + parent_email_sent: "Email enviado!" + parent_email_title: "Qual é o e-mail dos seus pais?" + parents: "Para os pais" + parents_title: "Seus filhos estão aprendendo a programar." # {change} + parents_blurb1: "Com o CodeCombat, seus filhos aprendem a programar de verdade. Eles começam a aprender comandos simples, e progridem para tópicos avançados." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "Apenas $9.99 USD/mês, eles recebem novos desafios todo mês e suporte no email pessoal de programadores profissionais." # {change} + parents_blurb3: "Sem risco: 100% devolução do dinheiro garantida, basta um simples clique em desinscrever-se." + payment_methods: "Formas de pagamento" + payment_methods_title: "Formas de pagamento aceitas" + payment_methods_blurb1: "Aceitamos cartões de crédito e Alipay no momento." + payment_methods_blurb2: "Se você precisa de outra forma de pagamento, por favor contate" + stripe_description: "Inscrição Mensal" + subscription_required_to_play: "Você precisará se inscrever para jogar este nível." + unlock_help_videos: "Inscreva-se para desbloquear todos os vídeos tutoriais." + personal_sub: "Inscrição individual" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" + will_be_cancelled: "Será cancelada em" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." + group_discounts: "Descontos para grupos" + group_discounts_1: "Nós também oferecemos descontos para grupos para inscrições em grande quantidades." +# group_discounts_1st: "1st subscription" + group_discounts_full: "Preço normal" +# group_discounts_2nd: "Subscriptions 2-11" + group_discounts_20: "20% de desconto" +# group_discounts_12th: "Subscriptions 12+" + group_discounts_40: "20% de desconto" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: choose_hero: "Escolha seu Herói" programming_language: "Linguagem de Programação" programming_language_description: "Qual Linguagem de Programação você gostaria de usar?" -# default: "Default" -# experimental: "Experimental" + default: "Padrão" + experimental: "Experimental" python_blurb: "Simples mas poderosa, Python é uma linguagem de programação de uso geral." javascript_blurb: "A linguagem da web." coffeescript_blurb: "Sintaxe de JavaScript mais legal." @@ -339,35 +478,48 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: lua_blurb: "Linguagem de script para jogos." io_blurb: "Simples mas obscura." status: "Status" + hero_type: "Tipo" weapons: "Armas" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" + weapons_warrior: "Espadas - Curta distância, Sem Magia" + weapons_ranger: "Bestas, Armas de fogo - Longa Distância, Sem Magia" + weapons_wizard: "Varinhas, Bastões - Longa Distância, Magia" + attack: "Ataque" # Can also translate as "Attack" health: "Vida" speed: "Velocidade" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + regeneration: "Cura" + range: "Distância de Ataque" # As in "attack or visual range" + blocks: "Bloqueio" # As in "this shield blocks this much damage" + backstab: "Dano de volta" # As in "this dagger does this much backstab damage" + skills: "Habilidades" + attack_1: "Retira" + attack_2: "das listadas" + attack_3: "dano da arma." + health_1: "Obtem" + health_2: "das listadas" + health_3: "saúde da armadura." + speed_1: "Se move para" + speed_2: "metros por segundo." + available_for_purchase: "Disponível para a Compra" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Nível para desbloquear:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Apenas alguns heróis podem jogar esse nível." -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + skill_docs: + writable: "gravável" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "apenas leitura" + action_name: "nome" + action_cooldown: "Demora" + action_specific_cooldown: "Recarregando" + action_damage: "Dano" + action_range: "Distância" + action_radius: "Raio" + action_duration: "Duração" + example: "Exemplo" + ex: "ex" # Abbreviation of "example" + current_value: "Valor Atual" + default_value: "Valor Padrão" + parameters: "Parametros" + returns: "Retorno" + granted_by: "Concebido por" save_load: granularity_saved_games: "Salvo" @@ -378,8 +530,6 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: volume_label: "Volume" music_label: "Música" music_description: "Ligar/desligar música de fundo." - autorun_label: "Rodar automaticamente" - autorun_description: "Controlar execução automática do código." editor_config: "Editor de Configurações" editor_config_title: "Editor de Configurações" editor_config_level_language_label: "Linguagem para este nível" @@ -400,7 +550,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: about: why_codecombat: "Por que CodeCombat?" - why_paragraph_1: "Precisa aprender a codificar? Você não precisa de aulas. Você precisa escrever muito código e se divertir fazendo isso." + why_paragraph_1: "Precisa aprender a programar? Você não precisa de aulas. Você precisa escrever muito código e se divertir fazendo isso." why_paragraph_2_prefix: "É disso que se trata a programação. Tem que ser divertido. Não divertido como" why_paragraph_2_italic: "oba uma insígnia" why_paragraph_2_center: "mas divertido como" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: press_paragraph_1_link: "Midia Kit" press_paragraph_1_suffix: ". Todas as logomarcas e imagens podem ser usadas sem nos contactar previamente." team: "Time" - george_title: "CEO" + george_title: "CEO" # {change} george_blurb: "Administrador" - scott_title: "Programador" + scott_title: "Programador" # {change} scott_blurb: "O Sensato" - nick_title: "Programador" + nick_title: "Programador" # {change} nick_blurb: "Guru Motivacional" michael_title: "Programador" michael_blurb: "Administrador de Sistemas" - matt_title: "Programador" + matt_title: "Programador" # {change} matt_blurb: "O Ciclista" + cat_title: "Chefe Artesão" + cat_blurb: "Corta-vento" + josh_title: "Game Designer" + josh_blurb: "O chão vai tremer" + jose_title: "Músico" + jose_blurb: "Sou descolado" + retrostyle_title: "Ilustração" + retrostyle_blurb: "Games estilo Retrô" + + teachers: + title: "CodeCombat para Professores" + intro_1: "CodeCombat é um jogo online que ensina programação. Estudantes criam código em linguagens de programação usadas na vida real." + intro_2: "Não é necessário ter experiência!" + free_title: "Quanto custa?" + cost_china: "CodeCombat na China é gratuito para os primeiros cinco níveis, depois disso o valor mensal para ter acesso a mais de 140 níveis nos nossos servidores exclusivos na China é de $9.99 dólares americanos." + free_1: "CodeCombat Basic é gratuito! Há mais de 80 níveis gratuitos que cobrem todos os conceitos." # {change} + free_2: "Uma assinatura mensal dá acesso aos vídeos tutoriais e mais níveis para praticar." + teacher_subs_title: "Professores recebem assinaturas gratuitas!" + teacher_subs_1: "Por favor contate" # {change} + teacher_subs_2: "para organizar uma assinatura mensal." # {change} +# teacher_subs_3: "to set up your subscription." + sub_includes_title: "O que está incluído na assinatura?" + sub_includes_1: "Além dos mais de 80 níveis básicos, estudantes com uma assinatura mensal têm acesso aos seguintes recursos:" # {change} + sub_includes_2: "Mais de 60 níveis para praticar" # {change} + sub_includes_3: "Vídeos tutoriais" + sub_includes_4: "Suporte premium por email" + sub_includes_5: "7 novos heróis com habilidades únicas a serem aprendidas" # {change} + sub_includes_6: "3500 gemas bônus todos os meses" + sub_includes_7: "Clãs Privados" + monitor_progress_title: "Como monitoro o progresso dos estudantes?" + monitor_progress_1: "O progresso dos estudantes pode ser monitorado ao criar" + monitor_progress_2: "para sua turma." + monitor_progress_3: "Para adicionar estudantes, envie-os o link de convite para seu clã, que está na" + monitor_progress_4: "página." + monitor_progress_5: "Depois que eles se juntarem ao seu Clã, você verá um resumo do progresso dos estudantes na página do seu Clã." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." + who_for_title: "Para quem é indicado o CodeCombat?" + who_for_1: "Nós recomendamos CodeCombat para estudantes a partir de 9 anos de idade. Nenhuma experiência anterior em programação é necessária." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." + sys_requirements_title: "Requisitos de Sistema" + sys_requirements_1: "Pelo motivo de CodeCombat ser um jogo, é mais intenso para ser processado em computadores do que tutoriais em vídeo ou escritos. Estamos otimizando para que rode rapidamente em todos navegadores modernos e também em computadores antigos, assim todos podem jogar. Sendo assim, aqui estão as nossas sugestões para tirar o máximo proveito da experiência de CodeCombat:" # {change} + sys_requirements_2: "Use versões novas dos navegadores Chrome ou Firefox." # {change} + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Salvar nova versão" new_major_version: "Nova versão principal" + submitting_patch: "Enviando Patch..." cla_prefix: "Para salvar as modificações, primeiro você deve concordar com nosso" cla_url: "CLA" cla_suffix: "." cla_agree: "EU CONCORDO" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contate-nos" welcome: "É bom escutar suas opiniões! Use este formulário para nos enviar um email." - contribute_prefix: "Se você se interessar em contribuir conosco, dê uma conferida na nossa " - contribute_page: "página de contribuição" - contribute_suffix: "!" forum_prefix: "Para algo público, por favor acesse " forum_page: "nosso fórum" forum_suffix: " ao invés disso." + faq_prefix: "Também há um" + faq: "FAQ" + subscribe_prefix: "Se você precisa de ajuda para resolver um nível, por favor" + subscribe: "compre uma assinatura de CodeCombat" + subscribe_suffix: "e teremos prazer em te ajudar com seu código." + subscriber_support: "Desde que você seja um assinante CodeCombat, seu e-mail será nossa prioridade de resposta." + screenshot_included: "Imagem da tela incluída." + where_reply: "Onde devemos responder?" send: "Enviar opinião" contact_candidate: "Contactar Candidato" # Deprecated recruitment_reminder: "Utilize esse formulário para entrar em contato com candidatos que você esteja interessado em entrevistar. Lembre-se que o CodeCombat cobra 15% do salário do primeiro ano. A taxa de contratação é cobrada quando da contratação do empregado e é reembolsável por 90 dias, se o empregado não permanece no emprego. Empregados de meio-turno, remotos ou com contrato serão gratuitos como estagiários." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: autosave: "As alterações serão salvas automaticamente." me_tab: "Eu" picture_tab: "Foto" + delete_account_tab: "Excluir sua conta" + wrong_email: "E-mail incorreto" + wrong_password: "Senha Incorreta" upload_picture: "Enviar uma foto" + delete_this_account: "Excluir essa conta definitivamente" + god_mode: "Modo Deus" password_tab: "Senha" emails_tab: "Emails" admin: "Admin" new_password: "Nova Senha" new_password_verify: "Confirmação" + type_in_email: "Digite seu e-mail para confirmar a exclusão" # {change} + type_in_password: "Digite sua senha." email_subscriptions: "Assinaturas para Notícias por Email" email_subscriptions_none: "Sem Assinaturas de Email" email_announcements: "Notícias" @@ -481,16 +732,15 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: job_profile_explanation: "Olá! Preencha tudo e iremos entrar em contato sobre encontrar um trabalho como desenvolvedor de software." sample_profile: "Veja um perfil de exemplo" view_profile: "Visualizar seu perfil" - wizard_tab: "Feiticeiro" - wizard_color: "Cor das Roupas do Feiticeiro" keyboard_shortcuts: keyboard_shortcuts: "Atalhos do Teclado" space: "Espaço" enter: "Enter" + press_enter: "pressione enter" escape: "Esc" shift: "Shift" -# run_code: "Run current code." + run_code: "Rodar código atual." run_real_time: "Rodar em tempo real." continue_script: "Pular script atual." skip_scripts: "Pular todos os scripts puláveis." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: toggle_pathfinding: "Ligar/desligar exibição do pathfinding (caminho)." beautify: "Embeleze seu código a partir da padronização de formatação." maximize_editor: "Maximizar/minimizar editor de código." - move_wizard: "Mova o Wizard pelo nível." community: main_title: "Comunidade CodeCombat" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: social_hipchat: "Converse conosco na sala pública do CodeCombat no HipChat" contribute_to_the_project: "Contribuir para o projeto" + clans: + clan: "Clã" + clans: "Clãs" + new_name: "Novo nome para o clã" + new_description: "Nova descrição para o clã" + make_private: "Tornar o clã privado" + subs_only: "Apenas para assinantes" + create_clan: "Criar novo clã" +# private_preview: "Preview" + public_clans: "Clãs Públicos" + my_clans: "Meus Clãs" + clan_name: "Nome do Clã" + name: "Nome" + chieftain: "Chefe" + type: "Tipo" + edit_clan_name: "Editar o nome do Clã" + edit_clan_description: "Editar a descrição do Clã" + edit_name: "Editar nome" + edit_description: "Editar descrição" + private: "(privado)" + summary: "Resumo" + average_level: "Média de nível" + average_achievements: "Médias de conquistas" + delete_clan: "Deletar Clã" + leave_clan: "Sair do Clã" + join_clan: "Juntar-se ao Clã" + invite_1: "Convidar:" + invite_2: "*Convide jogadores para este Clã enviando-os este link." + members: "Membros" + progress: "Progresso" + not_started_1: "Não iniciado" + started_1: "Iniciado" + complete_1: "Completado" + exp_levels: "Mostrar níveis" + rem_hero: "Remover herói" + status: "Estado" + complete_2: "Completado" + started_2: "Iniciado" + not_started_2: "Não Iniciado" + view_solution: "Clique para ver a solução." + latest_achievement: "Última Conquista" + playtime: "Tempo de Jogo" + last_played: "Último Jogo" + classes: archmage_title: "Arquimago" - archmage_title_description: "(Codificador)" + archmage_title_description: "(Programador)" + archmage_summary: "Se você é um desenvolvedor interessado em programar jogos educacionais, seja um arquimago e ajude-nos a construir CodeCombat!" artisan_title: "Artesão" artisan_title_description: "(Construtor de Nível)" + artisan_summary: "Construa e compartilhe níveis para você e seus amigos jogar. Seja um Artesão e aprenda a arte de lecionar outros à programar." adventurer_title: "Aventureiro" adventurer_title_description: "(Testador de Nível)" + adventurer_summary: "Obtenha nossos novos níveis (ou mesmo nosso conteúdo de assinante) de graça um semana adiantado e ajude-nos a remover bugs (erros) antes que eles sejam lançados publicamente." scribe_title: "Escriba" scribe_title_description: "(Escritor de Artigos)" + scribe_summary: "Bom código exige boa documentação. Escreva, edite, e melhore a documentação lida por milhões de jogadores ao redor do mundo." diplomat_title: "Diplomata" diplomat_title_description: "(Tradutor)" + diplomat_summary: "CodeCombat está disponível em mais de 45 idiomas graças aos nossos Diplomatas. Nos ajude você também, contribuindo com as traduções." ambassador_title: "Embaixador" ambassador_title_description: "(Suporte)" + ambassador_summary: "Acalme nossos usuários do fórum e forneça uma direção para suas dúvidas. Nossos embaixadores representam CodeCombat no mundo todo." editor: main_title: "Editores do CodeCombat" @@ -543,19 +842,29 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: thang_title: "Editor de Thang" level_title: "Editor de Nível" achievement_title: "Editor de Conquistas" + poll_title: "Editor de Enquete" back: "Voltar" revert: "Reverter" revert_models: "Reverter Modelos" pick_a_terrain: "Escolha um Terreno" - small: "Pequeno" + dungeon: "Masmorra" + indoor: "Interior" + desert: "Deserto" grassy: "Gramado" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Pequeno" + large: "Grande" fork_title: "Realizar um Novo Fork" fork_creating: "Criando Fork..." generate_terrain: "Gerando Terreno" more: "Mais" wiki: "Wiki" - live_chat: "Chat Ao Vivo" - level_some_options: "Algumas Opções?" + live_chat: "Chat ao Vivo" + thang_main: "Principal" + thang_spritesheets: "Planilhas de Sprites" + thang_colors: "Cores" + level_some_options: "Algumas Opções?" level_tab_thangs: "Thangs" level_tab_scripts: "Scripts" level_tab_settings: "Configurações" @@ -566,9 +875,14 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: level_tab_thangs_all: "Tudo" level_tab_thangs_conditions: "Condições de Início" level_tab_thangs_add: "Adicionar Thangs" + level_tab_thangs_search: "Buscar thangs" + add_components: "Adicionar componentes" + component_configs: "Configurações de componente" + config_thang: "Duplo-clique para configurar uma thang" delete: "Excluir" duplicate: "Duplicar" -# rotate: "Rotate" + stop_duplicate: "Parar de Duplicar" + rotate: "Rotacionar" level_settings_title: "Configurações" level_component_tab_title: "Componentess Atuais" level_component_btn_new: "Criar Novo Componente" @@ -588,37 +902,40 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: new_thang_title: "Criar um Novo Tipo de Thang" new_level_title: "Criar um Novo Nível" new_article_title_login: "Faça login para Criar um Novo Artigo" -# new_thang_title_login: "Log In to Create a New Thang Type" + new_thang_title_login: "Faça login para Criar um Novo Tipo de Thang" new_level_title_login: "Faça login para Criar um Novo Nível" new_achievement_title: "Criar Nova Conquista" new_achievement_title_login: "Faça login para Criar uma Nova Conquista" + new_poll_title: "Criar uma nova enquete" + new_poll_title_login: "Faça login para criar uma nova enquete" article_search_title: "Procurar Artigos Aqui" thang_search_title: "Procurar Tipos de Thang Aqui" level_search_title: "Procurar Níveis Aqui" achievement_search_title: "Buscar Conquistas" + poll_search_title: "Buscar enquetes" read_only_warning2: "Nota: você não pode salvar suas edições aqui pois não está logado." no_achievements: "Nenhuma conquista foi adicionada para esse nível ainda." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" + achievement_query_misc: "Conquista chave desligada dos variados" + achievement_query_goals: "Conquista chave desligada dos objetivos de nível" level_completion: "Conclusão do Nível" -# pop_i18n: "Populate I18N" + pop_i18n: "Popular I18N" + tasks: "Tarefas" + clear_storage: "Limpar suas alterações locais" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Prever" edit_article_title: "Editar Artigo " +# polls: +# priority: "Priority" + contribute: page_title: "Contribuindo" - character_classes_title: "Classes de Personagem" - introduction_desc_intro: "Nós temos grandes expectativas para o CodeCombat." - introduction_desc_pref: "Queremos ser o lugar onde os programadores de todos os tipos vêm para aprender e brincarem juntos, introduzir outros ao maravilhoso mundo da codificação, e refletir as melhores partes da comunidade. Não podemos e não queremos fazer isso sozinhos, o que faz de projetos como o GitHub, Stack Overflow e Linux ótimos são as pessoas que os utilizam e constróem sobre eles. Para esse fim, " - introduction_desc_github_url: "CodeCombat é totalmente código aberto" - introduction_desc_suf: ", e nosso objetivo é oferecer quantas maneiras forem possíveis para você participar e fazer deste projeto tanto seu como nosso." - introduction_desc_ending: "Nós esperamos que você se junte a nossa festa!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" + intro_blurb: "CodeCombat é 100% open source, ou seja, tem seu código aberto! Centenas de jogadores dedicados estão nos ajudando a construir o jogo dentro do que ele é hoje. Junte-se a nós e escreva o próximo capítulo na conquista de CodeCombat para ensinar o mundo a programar!" alert_account_message_intro: "Ei!" alert_account_message: "Para assinar os emails de classe, você precisa estar logado." - archmage_summary: "Interessado em trabalhar em gráficos do jogo, design de interface de usuário, banco de dados e organização de servidores, networking multijogador, física, som ou desempenho do motor de jogo? Quer ajudar a construir um jogo para ajudar outras pessoas a aprender o que você é bom? Temos muito a fazer e se você é um programador experiente e quer desenvolver para o CodeCombat, esta classe é para você. Gostaríamos muito de sua ajuda a construir o melhor jogo de programação da história." archmage_introduction: "Uma das melhores partes sobre a construção de jogos é que eles sintetizam diversas coisas diferentes. Gráficos, som, interação em tempo real, redes sociais, e, claro, muitos dos aspectos mais comuns da programação, desde a gestão em baixo nível de banco de dados, e administração do servidor até interação com o usuário e desenvolvimento da interface. Há muito a fazer, e se você é um programador experiente com um desejo ardente de realmente mergulhar no âmago da questão do CodeCombat, esta classe pode ser para você. Nós gostaríamos de ter sua ajuda para construir o melhor jogo de programação de todos os tempos." class_attributes: "Atributos da Classe" archmage_attribute_1_pref: "Conhecimento em " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: join_desc_4: "e começaremos a partir de lá!" join_url_email: "Envie-nos um email" join_url_hipchat: "Sala de bate-papo pública no HipChat" - more_about_archmage: "Saiba Mais Sobre Como Se Tornar Um Poderoso Arquimago" archmage_subscribe_desc: "Receba email sobre novas oportunidades para codificar e anúncios." - artisan_summary_pref: "Quer criar níveis e ampliar o arsenal do CodeCombat? As pessoas estão jogando com o nosso conteúdo em um ritmo mais rápido do que podemos construir! Neste momento, nosso editor de níveis é instável, então fique esperto. Fazer os níveis será um pouco desafiador e com alguns bugs. Se você tem visões de campanhas abrangendo for-loops para" - artisan_summary_suf: ", então essa classe é para você." artisan_introduction_pref: "Nós devemos contruir níveis adicionais! Pessoas estão clamando por mais conteúdo, e só podemos contruir tantos de nós mesmos. Agora sua estação de trabalho é o nível um; nosso Editor de Níveis é pouco utilizável até mesmo para seus criadores, então fique esperto. Se você tem visões de campanhas abrangendo for-loops para" artisan_introduction_suf: ", esta classe pode ser para você." artisan_attribute_1: "Qualquer experiência em construir conteúdo como esse seria legal, como usando os editores de nível da Blizzard. Mas não é obrigatório!" @@ -645,47 +959,37 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: artisan_join_step2: "Crie um novo nível e explore os níveis existentes." artisan_join_step3: "Encontre-nos na nossa sala pública no HipChat para ajuda." artisan_join_step4: "Publique seus níveis no fórum para avaliação." - more_about_artisan: "Saiba Mais Sobre Como Se Tornar Um Artesão Criativo" artisan_subscribe_desc: "Receba emails com novidades sobre o editor de níveis e anúncios." - adventurer_summary: "Vamos ser claros sobre o seu papel: você é o tanque. Você vai tomar o dano pesado. Precisamos de pessoas para experimentar os níveis inéditos e ajudar a identificar como fazer as coisas melhorarem. A dor será enorme, fazer bons jogos é um processo longo e ninguém acerta na primeira vez. Se você pode suportar isto e ter uma alta pontuação de constituição, então essa classe é para você." adventurer_introduction: "Vamos ser claros sobre o seu papel: você é o tanque. Você vai tomar dano pesado. Precisamos de pessoas para experimentar níveis inéditos e ajudar a identificar como fazer as coisas melhorarem. A dor será enorme, fazer bons jogos é um processo longo e ninguém acerta na primeira vez. Se você pode suportar e ter uma alta pontuação de constituição, então esta classe pode ser para você." adventurer_attribute_1: "Sede de aprendizado. Você quer aprender a codificar e nós queremos ensiná-lo a codificar. Você provavelmente vai fazer a maior parte do ensino neste caso." adventurer_attribute_2: "Carismático. Seja gentil, mas articulado sobre o que precisa melhorar, e ofereça sugestões sobre como melhorar." adventurer_join_pref: "Se reuna (ou recrute!) um Artesão e trabalhe com ele, ou marque a caixa abaixo para receber emails quando houver novos níveis para testar. Também estaremos postando sobre níveis que precisam de revisão em nossas redes como" adventurer_forum_url: "nosso fórum" adventurer_join_suf: "então se você prefere ser notificado dessas formas, inscreva-se lá!" - more_about_adventurer: "Saiba Mais Sobre Como Se Tornar Um Valente Aventureiro" adventurer_subscribe_desc: "Receba emails quando houver novos níveis para testar." - scribe_summary_pref: "O CodeCombat não será apenas um monte de níveis. Ele também será um recurso de conhecimento de programação que os jogadores podem se ligar. Dessa forma, cada artesão pode se vincular a um artigo detalhado para a edificação do jogador: documentação semelhante ao que o " - scribe_summary_suf: " construiu. Se você gosta de explicar conceitos de programação, então essa classe é para você." scribe_introduction_pref: "O CodeCombat não será apenas um monte de níveis. Ele também irá incluir uma fonte de conhecimento, um wiki de conceitos de programação que os níveis podem se basear. Dessa forma, em vez de cada Artesão ter que descrever em detalhes o que é um operador de comparação, eles podem simplesmente criar um link para o artigo que o descreve. Algo na linha do que a " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " construiu. Se a sua idéia de diversão é articular os conceitos de programação em Markdown, então esta classe pode ser para você." scribe_attribute_1: "Habilidade com palavras é praticamente tudo o que você precisa. Não só a gramática e ortografica, mas a capacidade de transmitir idéias muito complicadas para os outros." contact_us_url: "Contate-nos" scribe_join_description: "conte-nos um pouco sobre você, sua experiência com programação e que tipo de coisas você gostaria de escrever sobre. Nós começaremos a partir disso!" - more_about_scribe: "Saiba Mais Sobre Como Se Tornar Um Escriba Aplicado" scribe_subscribe_desc: "Receba email sobre anúncios de escrita de artigos." - diplomat_summary: "Há um grande interesse no CodeCombat em outros países que não falam inglês! Estamos à procura de tradutores que estão dispostos a gastar seu tempo traduzindo o corpo do site para que o CodeCombat esteja acessível para todo o mundo o mais rápido possível. Se você quiser ajudar a tornar o CodeCombat internacional, então essa classe é para você." diplomat_introduction_pref: "Então, se há uma coisa que aprendemos com o " diplomat_launch_url: "lançamento em Outubro" diplomat_introduction_suf: "é que há um interesse considerável no CodeCombat em outros países, especialmente no Brasil! Estamos construindo um corpo de tradutores ansiosos para transformar um conjunto de palavras em outro conjunto de palavras para tornar o CodeCombat tão acessível em todo o mundo quanto for possível. Se você gosta de obter cenas inéditas do próximo conteúdo e obter esses níveis para os seus compatriotas o mais rápido possível, então esta classe pode ser para você." diplomat_attribute_1: "Fluência no inglês e na língua para a qual você gostaria de traduzir. Ao transmitir idéias complicadas, é importante ter um forte domínio em ambos!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." + diplomat_i18n_page_prefix: "Você pode começar a traduzir nossos níveis indo para a" + diplomat_i18n_page: "página de tradução" + diplomat_i18n_page_suffix: ", ou nossa página no GitHub." diplomat_join_pref_github: "Encontre o arquivo de sua linguagem " diplomat_github_url: "no GitHub" diplomat_join_suf_github: ", edite-o online, e envie um pull request. Marque também, esta caixa abaixo para se manter atualizado sobre os novos desenvolvimento de internacionalização!" - more_about_diplomat: "Saiba Mais Sobre Como Se Tornar Um Ótimo Diplomata" diplomat_subscribe_desc: "Receba emails sobre o desenvolvimento da i18n e níveis para traduzir." - ambassador_summary: "Estamos tentando construir uma comunidade, e cada comunidade precisa de uma equipe de apoio quando há problemas. Temos chats, emails e redes sociais para que nossos usuários possam se familiarizar com o jogo. Se você quer ajudar as pessoas a se envolver, se divertir e aprender um pouco de programação, então essa classe é para você." ambassador_introduction: "Esta é uma comunidade que estamos construindo, e vocês são as conexões. Temos chats Olark, emails e redes sociais com muitas pessoas para conversar e ajudar a se familiarizar com o jogo e aprender. Se você quer ajudar as pessoas a se envolver e se divertir, e ter uma boa noção da pulsação do CodeCombat e para onde estamos indo em seguida, esta classe pode ser para você." ambassador_attribute_1: "Habilidades de comunicação. Ser capaz de identificar os problemas que os jogadores estão tendo e ajudar a resolvê-los, Além disso, manter o resto de nós informados sobre o que os jogadores estão dizendo, o que gostam e não gostam e do que querem mais!" ambassador_join_desc: "conte-nos um pouco sobre você, o que você fez e o que você estaria interessado em fazer. Nós começaremos a partir disso!" ambassador_join_note_strong: "Nota" ambassador_join_note_desc: "Uma das nossas principais prioridades é a construção de um multijogador onde os jogadores que estão com dificuldade para resolver um nível podem invocar feitiçeiros com nível mais alto para ajudá-los. Esta será uma ótima maneira para os embaixadores fazerem suas tarefas. Vamos mantê-lo informado!" - more_about_ambassador: "Saiba Mais Sobre Como Se Tornar Um Embaixador Prestativo" ambassador_subscribe_desc: "Receba emails sobre atualização do suporte e desenvolvimento do multijogador." changes_auto_save: "As alterações são salvas automaticamente quando você marcar as caixas de seleção." diligent_scribes: "Nossos Aplicados Escribas:" @@ -734,17 +1038,19 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: simple_ai: "IA Simples" warmup: "Aquecimento" friends_playing: "Amigos Jogando" - log_in_for_friends: "Faça loginp ara jogar com seus amigos!" + log_in_for_friends: "Faça login para jogar com seus amigos!" social_connect_blurb: "Conecte-se e jogue contra seus amigos!" - invite_friends_to_battle: "Convide seus amigos para juntarem-se à sua batalha!" + invite_friends_to_battle: "Convide seus amigos para se juntarem na batalha!" fight: "Lutem!" watch_victory: "Assista sua vitória" defeat_the: "Derrote" +# tournament_started: ", started" tournament_ends: "Fim do torneio" tournament_ended: "Torneio encerrado" tournament_rules: "Regras do Torneio" tournament_blurb: "Escreva códigos, colete ouro, construa exércitos, esmague inimigos, ganhe prêmios e aprimore sua carreira no nosso Torneio da Cobiça de $40,000! Veja os detalhes" tournament_blurb_criss_cross: "Ganhe leilões, construa caminhos, despiste oponentes, agarre joias e aprimore sua carreira no nosso Torneio de Cruzadas! Veja os detalhes" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" tournament_blurb_blog: "no nosso blog" rules: "Regras" winners: "Vencedores" @@ -763,14 +1069,15 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: no_achievements: "Nenhuma conquista ganha ainda." favorite_prefix: "Linguagem favorita é " favorite_postfix: "." + not_member_of_clans: "Ainda não é membro de nenhum clã." achievements: last_earned: "Últimos Ganhos" amount_achieved: "Montante" achievement: "Conquista" category_contributor: "Cotribuidor" -# category_ladder: "Ladder" -# category_level: "Level" + category_ladder: "Progressão" + category_level: "Nível" category_miscellaneous: "Diversos" category_levels: "Níveis" category_undefined: "Sem categoria" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: account: recently_played: "Jogos Recentes" no_recent_games: "Não foram feitos jogos durante duas semanas." + payments: "Pagamentos" + purchased: "Comprado" + subscription: "Assinatura" + invoices: "Faturas" + service_apple: "Apple" + service_web: "Web" + paid_on: "Pago" + service: "Serviço" + price: "Preço" + gems: "Gemas" + active: "Ativo" + subscribed: "Inscrito" + unsubscribed: "Desinscrito" + active_until: "Ativo até" + cost: "Custo" + next_payment: "Próximo Pagamento" + card: "Cartão" + status_unsubscribed_active: "Você não é um assinante e não será cobrado, mas sua conta ainda está ativa." + status_unsubscribed: "Ganhe acesso a novos níveis, heróis, itens, e gemas bônus com uma assinatura CodeCombat!" + + account_invoices: + amount: "Valor em dólares americanos" + declined: "Seu cartão foi recusado" + invalid_amount: "Coloque o valor em dólares americanos." + not_logged_in: "Entre no site com sua conta ou crie uma conta para acessar suas faturas." + pay: "Pagar Faturas" + purchasing: "Comprando..." + retrying: "Erro de servidor, tentando novamente." + success: "A transação foi completada com sucesso. Obrigado!" loading_error: could_not_load: "Erro ao carregar do servidor" @@ -792,7 +1128,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: unauthorized: "Você precisa estar autenticado. Você desativou os cookies?" forbidden: "Você não possui permissão." not_found: "Não encontrado." - not_allowed: "Método não permitodo." + not_allowed: "Método não permitido." timeout: "Tempo de requisição esgotado." conflict: "Conflito de recurso." bad_input: "Problema de entrada (bad input)." @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: leaderboard: "Tabela de Classificação" user_schema: "Esquema do Usuário" user_profile: "Perfil do Usuário" + patch: "Patch" patches: "Patches" patched_model: "Documento da Fonte" model: "Modelo" @@ -838,24 +1175,51 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: user_remarks: "Observações do Usuário" versions: "Versões" items: "Itens" + hero: "Herói" heroes: "Heróis" - wizard: "Assistente" achievement: "Conquista" clas: "CLAs" play_counts: "Contagem de Jogos" feedback: "Comentários" + payment_info: "Informação de pagamento" + campaigns: "Campanhas" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" delta: added: "Adicionado" modified: "Modificado" +# not_modified: "Not Modified" deleted: "Removido" moved_index: "Índice Movido" text_diff: "Diff do Texto" merge_conflict_with: "MERGE DO CONFLITO COM" no_changes: "Sem Alterações" -# guide: -# temp: "Temp" + guide: + temp: "Temp" multiplayer: multiplayer_title: "Configurações de Multijogador" # We'll be changing this around significantly soon. Until then, it's not important to translate. @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: practices_title: "Respeitáveis Boas Práticas" practices_description: "Essas são nossas promessas para você, o jogador, de uma maneira menos jurídica." privacy_title: "Privacidade" - privacy_description: "Nós não venderemos nenhuma de suas informações pessoais. Nós pretendemos ganhar dinheiro através de recrutamento eventualmente, mas fiquem tranquilos que nós não distribuiremos suas informações pessoais para companhias interessadas sem a sua aprovação explícita." + privacy_description: "Nós nâo venderemos suas informações pessoaos." security_title: "Segurança" security_description: "Nós lutamos para manter suas informações pessoais a salvo.Como um projeto de código aberto, nosso site é aberto para qualquer um rever e melhorar nossos sistemas de segurança." email_title: "Email" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: email_description_suffix: "ou através de links nos emails que enviarmos, você pode trocar as preferências e facilmente se desinscrever a qualquer hora." cost_title: "Custo" cost_description: "Atualmente o CodeCombat é 100% grátis. Um dos nossos principais objetivos é mantê-lo dessa forma, para que, o maior número possível de pessoas possa jogar, independente de seu lugar na vida. Se o céu escurecer, nós poderemos ter que cobrar uma assinatura, ou por algum conteúdo, mas preferimos que não. Com alguma sorte, nós seremos capazes de manter a empresa com:" - recruitment_title: "Recrutamento" - recruitment_description_prefix: "Aqui no CodeCombat, você vai se tornar um poderoso feiticeiro, não apenas no jogo, mas também na vida real." - url_hire_programmers: "Ninguém pode contratar programadores rápido o bastante" - recruitment_description_suffix: "então quando você aumentar suas habilidade e, se concordar, vamos demonstrar suas melhores realizações em codificação para os milhares de empregadores que estão babando para ter a chance de contratá-lo. Eles nos pagam um pouco, eles te pagam" - recruitment_description_italic: "muito" - recruitment_description_ending: "o site continua grátis e todo mundo fica feliz. Esse é o plano." copyrights_title: "Direitos Autorais e Licenças" contributor_title: "Contrato de Licença de Colaborador" contributor_description_prefix: "Todos os colaboradores, tanto no nosso site quando no nosso repositório no GitHub estão sujeitos ao nosso" @@ -938,7 +1296,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: rank: "Classificação" prizes: "Prêmios" total_value: "Valor Total" -# in_cash: "in cash" + in_cash: "Em dinheiro" custom_wizard: "Assistente Personalizado do CodeCombat" custom_avatar: "Avatar Personalizado do CodeCombat" heap: "para seis meses de acesso \"Startup\"" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: license: "licença" oreilly: "ebook de sua escolha" - wizard_settings: - title: "Configurações do Feiticeiro" - customize_avatar: "Personalize o seu Avatar" - active: "Ativo" - color: "Cor" - group: "Grupo" - clothes: "Roupas" - trim: "Aparar" - cloud: "Nuvem" - team: "Time" - spell: "Feitiço" - boots: "Botas" - hue: "Matiz" - saturation: "Saturação" - lightness: "Luminosidade" - account_profile: settings: "Configurações" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Edite seu perfil" @@ -1074,46 +1416,46 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: deprecation_warning: "Estamos nos concentrando em níveis iniciante, em vez de encontrar desenvolvedores especializados no momento." hire_developers_not_credentials: "Contrate desenvolvedores, não referências." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. get_started: "Comece" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." -# candidate_name: "Name" -# candidate_location: "Location" -# candidate_looking_for: "Looking For" -# candidate_role: "Role" -# candidate_top_skills: "Top Skills" -# candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" + already_screened: "Nós já selecionamos tecnicamente todos os nossos candidatos" + filter_further: ", mas você pode filtrar ainda mais:" + filter_visa: "Visa" + filter_visa_yes: "Autorizado nos EUA" + filter_visa_no: "Não autorizado" + filter_education_top: "Melhor colégio" + filter_education_other: "Outros" + filter_role_web_developer: "Desenvolvedor Web" + filter_role_software_developer: "Desenvolvedor de Software" + filter_role_mobile_developer: "Desenvolvedor Mobile" + filter_experience: "Experiência" + filter_experience_senior: "Sênior" + filter_experience_junior: "Júnior" + filter_experience_recent_grad: "Recentemente graduado" + filter_experience_student: "Estudante universitário" + filter_results: "resultados" + start_hiring: "Comece a contratar." + reasons: "Três razões para você buscar talentos através de nós:" + everyone_looking: "Todos aqui estão buscando por sua próxima oportunidade." + everyone_looking_blurb: "Esqueça sobre 20% dos índices de resposta do LinkedIn InMail. Todos que listamos neste site querem encontrar sua próxima posição e irão responder por sua solicitação de apresentação." + weeding: "Sente-se; nós capinamos por você." + weeding_blurb: "Cada jogador que nós listarmos foi devidamente filtrado por suas habilidades técnicas. Nós também fazemos contatos telefônicos para seleção de candidatos e fazemos anotações em seus perfís para economizar o seu tempo." + pass_screen: "Eles irão ser exibidos em sua tela técnica." + pass_screen_blurb: "Avalie o código de cada candidato antes de contatá-los. Um empregador descobriu que nossos desenvolvedores passaram 5x mais em seu filtro do que contratando através do Hacker News." + make_hiring_easier: "Faça minhas contratações mais fáceis, por favor." + what: "O que é CodeCombat?" + what_blurb: "CodeCombat é um jogo de programação multijogador que roda no navegador. Jogadores escrevem código para controlar suas forças na batalha contra outros desenvolvedores. Nossos jogadores tem experiênciacom todos os grandes stacks tecnológicos." + cost: "Quanto nós cobramos?" + cost_blurb: "Cobramos 15% do primeiro ano de salário e oferecemos garantia de 100% do dinheiro devolta por 90 dias. Não cobramos pelos candidatos que já estão sendo entrevistados na sua empresa." + candidate_name: "Nome" + candidate_location: "Localização" + candidate_looking_for: "Buscando por" + candidate_role: "Papel" + candidate_top_skills: "Melhores Habilidades" + candidate_years_experience: "Anos de Experiência" + candidate_last_updated: "Última Atualização" + candidate_who: "Quem" + featured_developers: "Desenvolvedores em destaque" + other_developers: "Outros Desenvolvedores" + inactive_developers: "Desenvolvedores Inativos" admin: av_espionage: "Espionagem" # Really not important to translate /admin controls. @@ -1127,10 +1469,10 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: av_entities_active_instances_url: "Instâncias Ativas" av_entities_employer_list_url: "Lista de Empregadores" av_entities_candidates_list_url: "Lista de Candidatos" -# av_entities_user_code_problems_list_url: "User Code Problems List" + av_entities_user_code_problems_list_url: "Problemas na Lista de Códigos do Usuário" av_other_sub_title: "Outro" av_other_debug_base_url: "Base (para debugar base.jade)" u_title: "Lista de Usuários" -# ucp_title: "User Code Problems" + ucp_title: "Problemas no Código do Usuário" lg_title: "Últimos Jogos" clas: "CLAs" diff --git a/app/locale/pt-PT.coffee b/app/locale/pt-PT.coffee index 596c2020b..fb90c71e3 100644 --- a/app/locale/pt-PT.coffee +++ b/app/locale/pt-PT.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: slogan: "Aprende a Programar ao Jogar um Jogo" no_ie: "O CodeCombat não funciona no Internet Explorer 8 ou anterior. Desculpa!" # Warning that only shows up in IE8 and older no_mobile: "O CodeCombat não foi feito para dispositivos móveis e pode não funcionar!" # Warning that shows up on mobile devices - play: "Jogar" # The big play button that just starts playing a level + play: "Jogar" # The big play button that opens up the campaign view. old_browser: "Ups, o teu navegador é demasiado antigo para que o CodeCombat funcione. Desculpa!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Mesmo assim podes tentar, mas provavelmente não irá funcionar." + ipad_browser: "Más notícias: o CodeCombat não funciona no navegador do iPad. Boas notícias: a nossa aplicação nativa para iPad está à espera da aprovação da Apple." campaign: "Campanha" for_beginners: "Para Iniciantes" multiplayer: "Multijogador" # Not currently shown on home page @@ -41,9 +42,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: diplomat_suggestion: title: "Ajuda a traduzir o CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "Precisamos das tuas habilidades linguísticas." - pitch_body: "Desenvolvemos o CodeCombat em Inglês, mas já temos jogadores em todo o mundo. Muitos deles querem jogar em Português e não falam Inglês, por isso, se sabes falar ambas, por favor considera registar-te como Diplomata para ajudares a traduzir o website do CodeCombat e todos os níveis para Português." + pitch_body: "Desenvolvemos o CodeCombat em Inglês, mas já temos jogadores de todo o mundo. Muitos deles querem jogar em Português, mas não falam Inglês, por isso, se sabes falar ambas, por favor considera registar-te como Diplomata para ajudares a traduzir o sítio do CodeCombat e todos os níveis para Português." missing_translations: "Enquanto não conseguirmos traduzir tudo para Português, irás ver em Inglês o que não estiver disponível em Português." - learn_more: "Sabe mais sobre ser um Diplomata" + learn_more: "Sabe mais sobre seres um Diplomata" subscribe_as_diplomat: "Subscreve-te como Diplomata" play: @@ -56,66 +57,64 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: confirm: "Confirmar" owned: "Obtido" # For items you own locked: "Bloqueado" + purchasable: "Adquirível" # For a hero you unlocked but haven't purchased available: "Disponível" skills_granted: "Habilidades Garantidas" # Property documentation details heroes: "Heróis" # Tooltip on hero shop button from /play achievements: "Conquistas" # Tooltip on achievement list button from /play account: "Conta" # Tooltip on account button from /play settings: "Definições" # Tooltip on settings button from /play + poll: "Votações" # Tooltip on poll button from /play next: "Seguinte" # Go from choose hero to choose inventory before playing a level change_hero: "Alterar Herói" # Go back from choose inventory to choose hero choose_inventory: "Equipar Itens" buy_gems: "Comprar Gemas" - older_campaigns: "Campanhas Mais Antigas" + subscription_required: "Subscrição Necessária" anonymous: "Jogador Anónimo" level_difficulty: "Dificuldade: " campaign_beginner: "Campanha para Iniciantes" - awaiting_levels_adventurer_prefix: "Nós adicionamos cinco níveis por semana." + awaiting_levels_adventurer_prefix: "Nós lançamos novos níveis todas as semanas." awaiting_levels_adventurer: "Regista-te como Aventureiro" awaiting_levels_adventurer_suffix: "para seres o primeiro a jogar níveis novos." - choose_your_level: "Escolhe o Teu Nível" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Podes saltar para um dos níveis abaixo ou discutir os níveis no " - adventurer_forum: "fórum do Aventureiro" - adventurer_suffix: "." - campaign_old_beginner: "Campanha para Iniciantes Antiga" - campaign_old_beginner_description: "... onde aprendes a magia da programação." - campaign_dev: "Níveis mais Difíceis Aleatórios" - campaign_dev_description: "... onde aprendes a interface enquanto fazes coisas um bocadinho mais difíceis." + adjust_volume: "Ajustar volume" campaign_multiplayer: "Arenas Multijogador" campaign_multiplayer_description: "... onde programas frente-a-frente contra outros jogadores." - campaign_player_created: "Criados por Jogadores" - campaign_player_created_description: "... onde combates contra a criatividade dos teus colegas <a href=\"/contribute#artisan\">Feiticeiros Artesãos</a>." - campaign_classic_algorithms: "Algoritmos Clássicos" - campaign_classic_algorithms_description: "... onde aprendes os algoritmos mais populares da Ciência da Computação." - campaign_forest: "Campanha da Floresta" - campaign_dungeon: "Campanha da Masmorra" + campaign_old_multiplayer: "(Obsoletas) Arenas Multijogador Antigas" + campaign_old_multiplayer_description: "Relíquias de uma idade mais civilizada. Não há simulações em curso para estas arenas multijogador, mais antigas e sem heróis." + + share_progress_modal: + blurb: "Estás a fazer grandes progressos! Conta ao teu educador o quanto aprendeste com o CodeCombat." + email_invalid: "Endereço de e-mail inválido." + form_blurb: "Introduz o e-mail dele abaixo e nós vamos mostrar-lhe!" + form_label: "Endereço de E-mail" + placeholder: "endereço de e-mail" + title: "Excelente Trabalho, Aprendiz" login: sign_up: "Criar Conta" log_in: "Iniciar Sessão" logging_in: "A Iniciar Sessão" log_out: "Sair" - recover: "recuperar conta" + forgot_password: "Esqueceste a tua palavra-passe?" authenticate_gplus: "Autenticar G+" load_profile: "Carregar Perfil do G+" - load_email: "Carregar E-mail do G+" finishing: "A terminar" + sign_in_with_facebook: "Iniciar sessão com o FB" + sign_in_with_gplus: "Iniciar sessão com o G+" + signup_switch: "Queres criar uma conta?" signup: - create_account_title: "Criar Conta para Guardar Progresso" - description: "É grátis. Só são necessárias umas coisas e fica tudo pronto:" email_announcements: "Receber anúncios por e-mail" - coppa: "Mais de 13 anos ou não estado-unidense " - coppa_why: "(Porquê?)" creating: "A Criar Conta..." sign_up: "Registar" log_in: "iniciar sessão com palavra-passe" social_signup: "Ou podes registar-te através do Facebook ou do Google+:" required: "Precisas de iniciar sessão antes de prosseguir dessa forma." + login_switch: "Já tens uma conta?" recover: recover_account_title: "Recuperar Conta" - send_password: "Enviar Password de Recuperação" + send_password: "Enviar Palavra-passe de Recuperação" recovery_sent: "E-mail de recuperação enviado." items: @@ -127,6 +126,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: books: "Livros" common: + back: "Voltar" # When used as an action verb, like "Navigate backward" + continue: "Continuar" # When used as an action verb, like "Continue forward" loading: "A carregar..." saving: "A guardar..." sending: "A enviar..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: fork: "Bifurcar" play: "Jogar" # When used as an action verb, like "Play next level" retry: "Tentar Novamente" + actions: "Ações" + info: "Informações" + help: "Ajuda" watch: "Vigiar" unwatch: "Desvigiar" submit_patch: "Submeter Versão" + submit_changes: "Submeter Alterações" + save_changes: "Guardar Alterações" general: and: "e" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: date: "Data" body: "Corpo" version: "Versão" - commit_msg: "Enviar Mensagem" + pending: "Pendentes" + accepted: "Aceites" + rejected: "Rejeitadas" + withdrawn: "Canceladas" + submitter: "Submissor" + submitted: "Submeteu" + commit_msg: "Mensagem da Submissão" + review: "Rever" version_history: "Histórico de Versões" version_history_for: "Histórico de Versões para: " + select_changes: "Seleciona duas das alterações abaixo para veres a diferença." + undo_prefix: "Desfazer" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Refazer" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Jogar pré-visualização do nível atual" result: "Resultado" results: "Resultados" description: "Descrição" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: hard: "Difícil" player: "Jogador" player_level: "Nível" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Guerreiro" + ranger: "Arqueiro" + wizard: "Feiticeiro" units: second: "segundo" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: reload_title: "Recarregar o Código Todo?" reload_really: "Tens a certeza que queres recarregar este nível de volta ao início?" reload_confirm: "Recarregar Tudo" + victory: "Vitória" victory_title_prefix: "" victory_title_suffix: " Concluído" victory_sign_up: "Criar Conta para Guardar Progresso" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: victory_rate_the_level: "Classifica este nível: " # Only in old-style levels. victory_return_to_ladder: "Voltar à Classificação" victory_play_continue: "Continuar" - victory_play_skip: "Passar à Frente" - victory_play_next_level: "Jogar Próximo Nível" - victory_play_more_practice: "Mais Treino" - victory_play_too_easy: "Muito Fácil" - victory_play_just_right: "Perfeito" - victory_play_too_hard: "Muito Difícil" victory_saving_progress: "A Guardar o Progresso" victory_go_home: "Ir para o Início" # Only in old-style levels. victory_review: "Conta-nos mais!" # Only in old-style levels. victory_hour_of_code_done: "Terminaste?" victory_hour_of_code_done_yes: "Sim, terminei a minha Hora do Código™!" + victory_experience_gained: "XP Ganho" + victory_gems_gained: "Gemas Ganhas" + victory_new_item: "Novo Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." + victory_become_a_viking: "Torna-te um Viking" guide_title: "Guia" tome_minion_spells: "Feitiços dos Seus Minions" # Only in old-style levels. tome_read_only_spells: "Feitiços Apenas de Leitura" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: tome_submit_button: "Submeter" tome_reload_method: "Recarregar o código original para este método" # Title text for individual method reload button. tome_select_method: "Selecionar um método" - tome_see_all_methods: "Ver todos os métodos editáveis" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "Ver todos os métodos editáveis" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Seleciona Alguém para " tome_available_spells: "Feitiços Disponíveis" tome_your_skills: "As Tuas Habilidades" + tome_help: "Ajuda" tome_current_method: "Método Atual" hud_continue_short: "Continuar" code_saved: "Código Guardado" @@ -254,21 +277,28 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: loading_ready: "Pronto!" loading_start: "Iniciar Nível" problem_alert_title: "Corrige o Teu Código" + problem_alert_help: "Ajuda" time_current: "Agora:" time_total: "Máximo:" time_goto: "Ir para:" + non_user_code_problem_title: "Impossível Carregar o Nível" + infinite_loop_title: "'Loop' Infinito Detetado" + infinite_loop_description: "O código inicial para construir o mundo nunca parou de ser executado. Provavelmente é muito lento ou contém um 'loop' infinito. Ou talvez haja um erro. Podes tentar executar este código novamente ou reiniciá-lo para o estado predefinido. Se isso não resultar, avisa-nos, por favor." + check_dev_console: "Também podes abrir a consola para programadores para veres o que possa estar a correr mal." + check_dev_console_link: "(instruções)" infinite_loop_try_again: "Tentar Novamente" infinite_loop_reset_level: "Reiniciar Nível" infinite_loop_comment_out: "Comentar o Meu Código" tip_toggle_play: "Alterna entre Jogar e Pausar com Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ rebobina e Ctrl+] avança." + tip_scrub_shortcut: "Usa Ctrl+[ para rebobinar e Ctrl+] para avançar." tip_guide_exists: "Clica no guia, dentro do menu do jogo (no topo da página), para informações úteis." tip_open_source: "O CodeCombat é 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "O CodeCombat lançou o seu beta em outubro de 2013." tip_think_solution: "Pensa na solução, não no problema." tip_theory_practice: "Teoricamente, não há diferença entre a teoria e a prática. Mas na prática, há. - Yogi Berra" tip_error_free: "Há duas formas de escrever programas sem erros; apenas a terceira funciona. - Alan Perlis" - tip_debugging_program: "Se depurar é o processo de remover erros, então programar deve ser o processo de os adicionar. - Edsger W. Dijkstra" + tip_debugging_program: "Se depurar é o processo de remover erros, então programar deve ser o processo de os introduzir. - Edsger W. Dijkstra" tip_forums: "Vai aos fóruns e diz-nos o que pensas!" tip_baby_coders: "No futuro, até os bebés serão Arcomagos." tip_morale_improves: "O carregamento irá continuar até que a moral melhore." @@ -282,20 +312,40 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: tip_no_try: "Fazer. Ou não fazer. Não há nenhum tentar. - Yoda" tip_patience: "Paciência tu deves ter, jovem Padawan. - Yoda" tip_documented_bug: "Um erro documentado não é um erro; é uma funcionalidade." - tip_impossible: "Parece sempre impossível até ser feito. - Nelson Mandela" + tip_impossible: "Tudo parece sempre impossível até ser feito. - Nelson Mandela" tip_talk_is_cheap: "Falar é fácil. Mostra-me o código. - Linus Torvalds" tip_first_language: "A coisa mais desastrosa que podes aprender é a tua primeira linguagem de programação. - Alan Kay" tip_hardware_problem: "P: Quantos programadores são necessários para mudar uma lâmpada? R: Nenhum, é um problema de hardware." tip_hofstadters_law: "Lei de Hofstadter: Tudo demora sempre mais do que pensas, mesmo quando levas em conta a Lei de Hofstadter." tip_premature_optimization: "Uma otimização permatura é a raíz de todo o mal. - Donald Knuth" tip_brute_force: "Quando em dúvida, usa a força bruta. - Ken Thompson" - customize_wizard: "Personalizar Feiticeiro" + tip_extrapolation: "Há apenas dois tipos de pessoas: aquelas que conseguem tirar uma conclusão a partir de dados reduzidos..." + tip_superpower: "A programação é a coisa mais próxima de um superpoder que temos." + tip_control_destiny: "Em 'open source' a sério, tens o direito de controlares o teu próprio destino. - Linus Torvalds" + tip_no_code: "Nenhum código é mais rápido que código não existente." + tip_code_never_lies: "O código nunca mente, mas os comentários às vezes sim. — Ron Jeffries" + tip_reusable_software: "Antes de um software poder ser reutilizável, primeiro tem de ser utilizável." + tip_optimization_operator: "Todas as linguagens têm um operador de otimização. Na maior parte delas esse operador é ‘//’." + tip_lines_of_code: "Medir o progresso em programação pelo número de linhas de código é como medir o progresso da construção de um avião pelo peso. — Bill Gates" + tip_source_code: "Quero mudar o mundo, mas não há maneira de me darem o código-fonte." + tip_javascript_java: "Java é para JavaScript o mesmo que Carro (Car) para Tapete (Carpet). - Chris Heilmann" + tip_move_forward: "Faças o que fizeres, segue em frente. - Martin Luther King Jr" + tip_google: "Tens um problema que não consegues resolver? Vai ao Google!" + tip_adding_evil: "A acrescentar uma pitada de mal." + tip_hate_computers: "É o problema das pessoas que acham que odeiam coputadores. O que elas odeiam mesmo são maus programadores. - Larry Niven" + tip_open_source_contribute: "Podes ajudar a melhorar o CodeCombat!" + tip_recurse: "Iterar é humano, recursar é divino. - L. Peter Deutsch" + tip_free_your_mind: "Tens de libertar tudo, Neo. Medo, dúvida e descrença. Liberta a tua mente. - Morpheus" + tip_strong_opponents: "Até o mais forte dos adversários tem uma fraqueza. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Inventário" save_load_tab: "Guardar/Carregar" options_tab: "Opções" guide_tab: "Guia" + guide_video_tutorial: "Tutorial em Vídeo" + guide_tips: "Dicas" multiplayer_tab: "Multijogador" auth_tab: "Regista-te" inventory_caption: "Equipa o teu herói" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: multiplayer_caption: "Joga com amigos!" auth_caption: "Guarda o teu progresso." + leaderboard: + leaderboard: "Tabela de Classificação" + view_other_solutions: "Ver Tabelas de Classificação" + scores: "Pontuações" + top_players: "Melhores Jogadores por" + day: "Hoje" + week: "Esta Semana" + all: "Sempre" + time: "Tempo" + damage_taken: "Dano Recebido" + damage_dealt: "Dano Infligido" + difficulty: "Dificuldade" + gold_collected: "Ouro Recolhido" + inventory: choose_inventory: "Equipar Itens" equipped_item: "Equipado" + required_purchase_title: "Necessário" available_item: "Disponível" restricted_title: "Restrito" should_equip: "(clica duas vezes para equipares)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: purchasing: "A adquirir..." declined: "O teu cartão foi recusado." retrying: "Erro do servidor, a tentar novamente." + prompt_title: "Sem Gemas Suficientes" + prompt_body: "Queres obter mais?" + prompt_button: "Entra na Loja" + recovered: "Recuperada a compra de gemas anterior. Por favor atualiza a página." + price: "x3500 / mês" + + subscribe: + comparison_blurb: "Aperfeiçoa as tuas habilidades com uma subscrição do CodeCombat!" + feature1: "100+ níveis básicos dispersos por 4 mundos" + feature2: "10 <strong>heróis novos</strong> e poderosos com habilidades únicas!" + feature3: "70+ níveis de bónus" + feature4: "<strong>3500 gemas de bónus</strong> por mês!" + feature5: "Tutoriais em vídeo" + feature6: "Apoio por e-mail superior" + feature7: "<strong>Clãs</strong> Privados" + free: "Grátis" + month: "mês" + subscribe_title: "Subscrever" + unsubscribe: "Cancelar Subscrição" + confirm_unsubscribe: "Confirmar Cancelamento da Subscrição" + never_mind: "Não Importa, Gostamos de Ti à Mesma" + thank_you_months_prefix: "Obrigado por nos teres apoiado neste(s) último(s)" + thank_you_months_suffix: "mês(meses)." + thank_you: "Obrigado por apoiares o CodeCombat." + sorry_to_see_you_go: "Lamentamos ver-te partir! Por favor, diz-nos o que podíamos ter feito melhor." + unsubscribe_feedback_placeholder: "Oh, o que fomos fazer?" + parent_button: "Pergunta ao teu educador" + parent_email_description: "Vamos mandar-lhe um e-mail para que ele possa comprar-te uma subscrição do CodeCombat." + parent_email_input_invalid: "Endereço de e-mail inválido." + parent_email_input_label: "Endereço de e-mail do educador" + parent_email_input_placeholder: "Introduz o e-mail do educador" + parent_email_send: "Enviar E-mail" + parent_email_sent: "E-mail enviado!" + parent_email_title: "Qual é o e-mail do teu educador?" + parents: "Para Educadores" + parents_title: "Caro Educador: O seu educando está a aprender a programar. Vai ajudá-lo a continuar?" + parents_blurb1: "O seu educando já jogou __nLevels__ níveis e aprendeu as bases da programação. Ajude a desenvolver o interesse dele, comprando-lhe uma subscrição para que possa continuar a jogar." + parents_blurb1a: "A programação de computadores é uma habilidade fundamental que o seu educando vai usar incontestavelmente quando for adulto. Em 2020, habilidades de programação básicas serão requisitadas por 77% dos empregos e, atualmente, há uma grande procura por engenheiros informáticos no mundo. Sabia que os cursos universitários ligados às Ciências da Computação são os mais bem pagos?" + parents_blurb2: "Por $9.99 USD/mês, o seu educando recebe novos desafios todas as semanas e suporte pessoal, via e-mail, de programadores profissionais." + parents_blurb3: "Sem Risco: 100% de garantia de devolução do dinheiro, com anulação fácil de 1 clique." + payment_methods: "Formas de Pagamento" + payment_methods_title: "Formas de Pagamento Aceites" + payment_methods_blurb1: "Atualmente aceitamos cartões de crédito e Alipay." + payment_methods_blurb2: "Se precisares de uma outra forma de pagamento, por favor contacta" + stripe_description: "Subscrição Mensal" + subscription_required_to_play: "Precisas de uma subscrição para jogares este nível." + unlock_help_videos: "Subscreve-te para desbloqueares todos os tutoriais em vídeo." + personal_sub: "Subscrição Pessoal" # Accounts Subscription View below + loading_info: "A carregar as informações da subscrição..." + managed_by: "Gerida por" + will_be_cancelled: "Será cancelada em" + currently_free: "Atualmente tens uma subscrição gratuita" + currently_free_until: "Atualmente tens uma subscrição gratuita até" + was_free_until: "Tinhas uma subscrição gratuita até" + managed_subs: "Subscrições Geridas" + managed_subs_desc: "Adiciona subscrições para outros jogadores (estudantes, crianças, etc.)" + managed_subs_desc_2: "Estes jogadores devem ter uma conta do CodeCombat associada ao endereço de e-mail que forneceres." + group_discounts: "Descontos de grupo" + group_discounts_1: "Também oferecemos descontos de grupo para subscrições em massa." + group_discounts_1st: "1ª subscrição" + group_discounts_full: "Preço total" + group_discounts_2nd: "2ª-11ª subscrições" + group_discounts_20: "20% de desconto" + group_discounts_12th: "12ª+ subscrições" + group_discounts_40: "40% de desconto" + subscribing: "A subscrever..." + recipient_emails_placeholder: "Introduz endereços de e-mail para subscrever, um por linha." + subscribe_users: "Subscrever Utilizadores" + users_subscribed: "Utilizadores subscritos:" + no_users_subscribed: "Nenhum utilizador subscrito. Por favor, verifica os endereços de e-mail." + current_recipients: "Beneficiários Atuais" + unsubscribing: "A Cancelar a Subscrição..." + subscribe_prepaid: "Clica em Subscrever para usares um código pré-pago" + using_prepaid: "A usar um código pré-pago para a subscrição mensal" choose_hero: choose_hero: "Escolhe o Teu Herói" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: lua_blurb: "Linguagem para scripts de jogos." io_blurb: "Simples mas obscuro." status: "Estado" + hero_type: "Tipo" weapons: "Armas" weapons_warrior: "Espadas - Curto Alcance, Sem Magia" weapons_ranger: "Arcos, Armas - Longo Alcance, Sem Magia" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: regeneration: "Regeneração" range: "Alcance" # As in "attack or visual range" blocks: "Bloqueia" # As in "this shield blocks this much damage" + backstab: "Colateral" # As in "this dagger does this much backstab damage" skills: "Habilidades" + attack_1: "Dá" + attack_2: "do dano da arma do" + attack_3: "apresentado." + health_1: "Ganha" + health_2: "da vida da armadura do" + health_3: "apresentado." + speed_1: "Move a" + speed_2: "metros por segundo." + available_for_purchase: "Disponível para Aquirir" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Nível para desbloquear:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Apenas certos heróis podem jogar este nível." skill_docs: writable: "escrevível" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: volume_label: "Volume" music_label: "Música" music_description: "Ativar ou desativar a música de fundo." - autorun_label: "Executar Automaticamente" - autorun_description: "Controlar a execução automática do código." editor_config: "Configurar Editor" editor_config_title: "Configurar Editor" editor_config_level_language_label: "Linguagem para Este Nível" @@ -400,46 +550,140 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: about: why_codecombat: "Porquê o CodeCombat?" - why_paragraph_1: "Se queres aprender a programar, não precisas de aulas. Precisas sim de escrever muito código e passar um bom bocado enquanto o fazes." - why_paragraph_2_prefix: "Afinal, é sobre isso que é a programação. Tem de ser divertida. Não divertida do género" - why_paragraph_2_italic: "yay uma medalha" - why_paragraph_2_center: "mas sim divertida do género" - why_paragraph_2_italic_caps: "NÃO MÃE, TENHO DE ACABAR O NÍVEL!" - why_paragraph_2_suffix: "É por isso que o CodeCombat é um jogo multijogador, e não um jogo que não passa de um curso com lições. Nós não vamos parar enquanto não puderes parar--mas desta vez, isso é uma coisa boa." + why_paragraph_1: "Se queres aprender a programar, não precisas de aulas. Precisas é de escrever muito código e divertires-te enquanto o fazes." + why_paragraph_2_prefix: "É sobre isso que é a programação. Tem de ser divertida. Não divertida do género" + why_paragraph_2_italic: "'yay, uma medalha'" + why_paragraph_2_center: "mas divertida do género" + why_paragraph_2_italic_caps: "'NÃO MÃE, TENHO DE ACABAR O NÍVEL!'" + why_paragraph_2_suffix: "É por isso que o CodeCombat é um jogo multijogador e não um jogo que não passa de um curso com lições. Nós não vamos parar enquanto não puderes parar--mas desta vez, isso é uma coisa boa." why_paragraph_3: "Se vais ficar viciado em algum jogo, vicia-te neste e torna-te num dos feiticeiros da idade da tecnologia." press_title: "Bloggers/Imprensa" - press_paragraph_1_prefix: "Queres escrever sobre nós? Sente-te à vontade para descarregar e usar todos os recursos incluídos no nosso" + press_paragraph_1_prefix: "Queres escrever sobre nós? Sente-te à vontade para descarregares e usares todos os recursos incluídos no nosso" press_paragraph_1_link: "pacote de imprensa" press_paragraph_1_suffix: ". Todos os logótipos e imagens podem ser usados sem sermos contactados diretamente." team: "Equipa" - george_title: "CEO" + george_title: "Co-fundador" george_blurb: "Homem de Negócios" - scott_title: "Programador" + scott_title: "Co-fundador" scott_blurb: "O Sensato" - nick_title: "Programador" + nick_title: "Co-fundador" nick_blurb: "Guru da Motivação" michael_title: "Programador" michael_blurb: "Administrador do Sistema" - matt_title: "Programador" + matt_title: "Co-fundador" matt_blurb: "Ciclista" + cat_title: "Artesã Chefe" + cat_blurb: "Mágica" + josh_title: "Designer de Jogos" + josh_blurb: "Floor Is Lava" + jose_title: "Música" + jose_blurb: "Taking Off" + retrostyle_title: "Ilustração" + retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat: Informações para Professores" + intro_1: "O CodeCombat é um jogo 'online' que ensina programação. Os estudantes escrevem código em linguagens de programação reais." + intro_2: "Não é necessário ter experiência!" + free_title: "Quanto custa?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." + free_1: "Há 100+ níveis GRATUITOS que abordam cada conceito." + free_2: "Uma subscrição mensal garante acesso a tutoriais em vídeo e a níveis extra para praticar." + teacher_subs_title: "Os professores recebem uma subscrição gratuita!" + teacher_subs_1: "Por favor, preenche o nosso" + teacher_subs_2: "Inquérito para Professores" + teacher_subs_3: "para configurares a tua subscrição." + sub_includes_title: "O que está incluído na subscrição?" + sub_includes_1: "Para além dos 100+ níveis básicos, os estudantes com uma subscrição mensal têm acesso às seguintes funcionalidades adicionais:" + sub_includes_2: "70+ níveis para praticar" + sub_includes_3: "Tutoriais em vídeo" + sub_includes_4: "Suporte por e-mail prioritário" + sub_includes_5: "10 novos heróis com habilidades únicas para dominar" + sub_includes_6: "3500 gemas de bónus todos os meses" + sub_includes_7: "Clãs Privados" + monitor_progress_title: "Como é que acompanho o progresso dos estudantes?" + monitor_progress_1: "O progresso dos estudantes pode ser acompanhado ao criares um" + monitor_progress_2: "para a tua turma." + monitor_progress_3: "Para adicionares um estudante, envia-lhe a ligação de convite para o teu Clã, a qual podes encontrar através da página dos" + monitor_progress_4: ", ao acederes ao teu." + monitor_progress_5: "Depois de ele se juntar, verás um resumo do progresso do estudante na tua página do Clã." + private_clans_1: "Os Clãs Privados garantem privacidade e informações sobre o progresso de cada estudante superiores." + private_clans_2: "Para criares um Clã privado, marca a caixa 'Tornar o clã privado' aquando da criação de um" + private_clans_3: "." + who_for_title: "Para quem é o CodeCombat?" + who_for_1: "Recomendamos o CodeCombat para estudantes com idade superior a 9. Não é necessária nenhuma experiência de programação prévia." + who_for_2: "O CodeCombat foi pensado de forma a ser apelativo tanto para rapazes como para raparigas." + material_title: "Quanto material há?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." + material_1: "Aproximadamente 25 horas de conteúdo gratuito e 15 horas adicionais de conteúdo para subscritores." + concepts_title: "Que conceitos são abordados?" + how_much_title: "Quanto custa uma subscrição mensal?" + how_much_1: "Uma" + how_much_2: "subscrição mensal" + how_much_3: "custa $9.99 e pode ser cancelada a qualquer momento." + how_much_4: "Adicionalmente, oferecemos descontos para grupos maiores:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." + more_info_title: "Onde posso encontrar mais informação?" + more_info_1: "O nosso" + more_info_2: "fórum para professores" + more_info_3: "é um bom sítio para te ligares a outros professores que também usam o CodeCombat." + sys_requirements_title: "Requisitos do Sistema" + sys_requirements_1: "Um navegador moderno. As versões mais recentes do Chrome, Firefox ou Safari. Internet Explorer 9 ou mais recente." + sys_requirements_2: "O CodeCombat ainda não é suportado em iPad's." + + teachers_survey: + title: "Inquérito para Professores" + must_be_logged: "Primeiro tens de ter sessão iniciada. Por favor, cria uma conta ou inicia sessão a partir do menu acima." + retrieving: "A recolher informações..." + being_reviewed_1: "A tua aplicação para uma subscrição de avaliação gratuita está a ser" + being_reviewed_2: "revista." + approved_1: "A tua aplicação para uma subscrição de avaliação gratuita foi" + approved_2: "aprovada." + approved_3: "Mais instruções foram enviadas para" + denied_1: "A tua aplicação para uma subscrição de avaliação gratuita foi" + denied_2: "recusada." + contact_1: "Por favor, contacta" + contact_2: "se tiveres mais questões." + description_1: "Oferecemos subscrições gratuitas a professores para efeitos de avaliação. Na nossa página para" + description_2: "professores" + description_3: "podes encontar muitas mais informações." + description_4: "Por favor, preenche este pequeno inquérito e nós enviar-te-emos, por e-mail, as instruções de configuração." + email: "Endereço de E-mail" + school: "Nome da Escola" + location: "Nome da Cidade" + age_students: "Que idade têm os teus alunos?" + under: "Menos de" + other: "Outra:" + amount_students: "Quantos alunos ensinas?" + hear_about: "Como ouviste falar do CodeCombat?" + fill_fields: "Por favor, preenche todos os campos." + thanks: "Obrigado! Vamos enviar-te as instruções de configuração em breve." versions: save_version_title: "Guardar Nova Versão" new_major_version: "Nova Versão Principal" + submitting_patch: "A Submeter Atualização..." cla_prefix: "Para guardares as alterações, precisas de concordar com o nosso" cla_url: "CLA" cla_suffix: "." cla_agree: "EU CONCORDO" + owner_approve: "Um administrador terá de aprová-la antes de as tuas alterações ficarem visíveis." contact: contact_us: "Contacta o CodeCombat" welcome: "É bom ter notícias tuas! Usa este formulário para nos enviares um e-mail. " - contribute_prefix: "Se estás interessado em contribuir, dá uma olhadela à nossa " - contribute_page: "página de contribuição" - contribute_suffix: "!" forum_prefix: "Para algo público, por favor usa o " forum_page: "nosso fórum" forum_suffix: " como alternativa." + faq_prefix: "Há também uma" + faq: "FAQ" + subscribe_prefix: "Se precisas de ajuda a perceber um nível, por favor" + subscribe: "compra uma subscrição do CodeCombat" + subscribe_suffix: "e nós ficaremos felizes por ajudar-te com o teu código." + subscriber_support: "Como és um subscritor do CodeCombat, os teus e-mails terão prioridade no nosso suporte." + screenshot_included: "Captura de ecrã incluída." + where_reply: "Para onde devemos enviar a resposta?" send: "Enviar Feedback" contact_candidate: "Contactar Candidato" # Deprecated recruitment_reminder: "Usa este formulário para chegares a candidatos que estejas interessado em entrevistar. Lembra-te que o CodeCombat cobra 15% do salário do primeiro ano. A taxa é cobrada no momento da contratação do empregado e é reembolsável durante 90 dias, no caso de o trabalhador não se manter empregado. A empregados em part-time, no estrangeiro e a contrato não são aplicadas taxas, porque são internos." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: autosave: "Alterações São Guardadas Automaticamente" me_tab: "Eu" picture_tab: "Fotografia" + delete_account_tab: "Elimina a Tua Conta" + wrong_email: "E-mail Errado" + wrong_password: "Palavra-passe Errada" upload_picture: "Anexar uma fotografia" + delete_this_account: "Elimina esta conta permanentemente" + god_mode: "Modo Deus" password_tab: "Palavra-passe" emails_tab: "E-mails" admin: "Administrador" new_password: "Nova Palavra-passe" new_password_verify: "Verificar" + type_in_email: "Escreve o teu e-mail para confirmares a eliminação." + type_in_password: "Escreve também a tua palavra-passe." email_subscriptions: "Subscrições de E-mail" email_subscriptions_none: "Sem Subscições de E-mail." email_announcements: "Anúncios" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: job_profile_explanation: "Olá! Preenche isto e entraremos em contacto contigo sobre encontrar um emprego de desenvolvedor de software para ti." sample_profile: "Vê um exemplo de perfil" view_profile: "Vê o Teu Perfil" - wizard_tab: "Feiticeiro" - wizard_color: "Cor das Roupas do Feiticeiro" keyboard_shortcuts: keyboard_shortcuts: "Atalhos de Teclado" space: "Espaço" enter: "Enter" + press_enter: "pressiona enter" escape: "Esc" shift: "Shift" run_code: "Executar código atual." @@ -503,16 +753,15 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: toggle_pathfinding: "Ativar/desativar a sobreposição do encontrador de caminho." beautify: "Embelezar o código ao estandardizar a formatação." maximize_editor: "Maximizar/minimizar o editor de código." - move_wizard: "Mover o Feiticeiro pelo nível." community: main_title: "Comunidade do CodeCombat" - introduction: "Confere abaixo as formas de te envolveres e decide o que te parece melhor. Estamos ansiosos por trabalhar contigo!" + introduction: "Confere abaixo as formas de te envolveres e escolhe a que te parece mais divertida. Estamos ansiosos por trabalhar contigo!" level_editor_prefix: "Usa o" - level_editor_suffix: "do CodeCombat para criares e editares níveis. Os utilizadores já criaram níveis para aulas, amigos, maratonas hacker, estudantes e familiares. Se criar um nível parece intimidante, podes começar por bifurcar um dos nossos!" + level_editor_suffix: "do CodeCombat para criares e editares níveis. Os utilizadores já criaram níveis para aulas, amigos, maratonas hacker, estudantes e familiares. Se criar um nível novo parece intimidante, podes começar por bifurcar um dos nossos!" thang_editor_prefix: "Chamamos 'thangs' às unidades do jogo. Usa o" - thang_editor_suffix: "para modificares a arte do CodeCombat. Dá permição às unidades para lançarem projéteis, altera a direção de uma animação, altera os pontos de vida de uma unidade ou anexa as tuas próprias unidades." - article_editor_prefix: "Vês um erro em alguns dos nossos documentos? Queres escrever algumas instruções para as tuas criações? Confere o" + thang_editor_suffix: "para modificares a arte do CodeCombat. Faz as unidades lançarem projéteis, altera a direção de uma animação, altera os pontos de vida de uma unidade ou anexa as tuas próprias unidades." + article_editor_prefix: "Vês um erro em alguns dos nossos documentos? Queres escrever algumas instruções para as tuas próprias criações? Confere o" article_editor_suffix: "e ajuda os jogadores do CodeCombat a obter o máximo do tempo de jogo deles." find_us: "Encontra-nos nestes sítios" social_blog: "Lê o blog do CodeCombat no Sett" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: social_hipchat: "Fala connosco na sala pública HipChat do CodeCombat" contribute_to_the_project: "Contribui para o projeto" + clans: + clan: "Clã" + clans: "Clãs" + new_name: "Nome do novo clã" + new_description: "Descrição do novo clã" + make_private: "Tornar o clã privado" + subs_only: "apenas para subscritores" + create_clan: "Criar um Novo Clã" + private_preview: "Pré-visualização" + public_clans: "Clãs Públicos" + my_clans: "Os Meus Clãs" + clan_name: "Nome do Clã" + name: "Nome" + chieftain: "Líder" + type: "Tipo" + edit_clan_name: "Editar Nome do Clã" + edit_clan_description: "Editar Descrição do Clã" + edit_name: "editar nome" + edit_description: "editar descrição" + private: "(privado)" + summary: "Resumo" + average_level: "Nível em Média" + average_achievements: "Conquistas em Média" + delete_clan: "Eliminar o Clã" + leave_clan: "Abandonar o Clã" + join_clan: "Entrar no Clã" + invite_1: "Convidar:" + invite_2: "*Convida jogadores para este Clã enviando-lhes esta ligação." + members: "Membros" + progress: "Progresso" + not_started_1: "não começado" + started_1: "começado" + complete_1: "completo" + exp_levels: "Expandir os níveis" + rem_hero: "Remover Herói" + status: "Estado" + complete_2: "Completo" + started_2: "Começado" + not_started_2: "Não Começado" + view_solution: "Clica para veres a solução." + latest_achievement: "Última Conquista" + playtime: "Tempo de jogo" + last_played: "Última vez jogado" + classes: archmage_title: "Arcomago" archmage_title_description: "(Programador)" + archmage_summary: "Se és um programador interessado em programar jogos educacionais, torna-te um Arcomago para nos ajudares a construir o CodeCombat!" artisan_title: "Artesão" artisan_title_description: "(Construtor de Níveis)" + artisan_summary: "Constrói e partilha níveis para tu e os teus amigos jogarem. Torna-te um Artesão para aprenderes a arte de ensinar outros a programar." adventurer_title: "Aventureiro" adventurer_title_description: "(Testador de Níveis)" + adventurer_summary: "Recebe os nossos novos níveis (até o conteúdo para subscritores) de graça, uma semana antes, e ajuda-nos a descobrir erros antes do lançamento para o público." scribe_title: "Escrivão" scribe_title_description: "(Editor de Artigos)" + scribe_summary: "Bom código precisa de uma boa documentação. Escreve, edita e melhora os documentos lidos por milhões de jogadores pelo mundo." diplomat_title: "Diplomata" diplomat_title_description: "(Tradutor)" + diplomat_summary: "O CodeCombat está traduzido em 45+ idiomas graças aos nossos Diplomatas. Ajuda-nos e contribui com traduções." ambassador_title: "Embaixador" ambassador_title_description: "(Suporte)" + ambassador_summary: "Amansa os nossos utilizadores do fórum e direciona aqueles que têm questões. Os nossos Embaixadores representam o CodeCombat perante o mundo." editor: main_title: "Editores do CodeCombat" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: thang_title: "Editor de Thangs" level_title: "Editor de Níveis" achievement_title: "Editor de Conquistas" + poll_title: "Editor de Votações" back: "Voltar" revert: "Reverter" revert_models: "Reverter Modelos" pick_a_terrain: "Escolhe Um Terreno" + dungeon: "Masmorra" + indoor: "Interior" + desert: "Deserto" + grassy: "Relvado" +# mountain: "Mountain" +# glacier: "Glacier" small: "Pequeno" - grassy: "Com Relva" + large: "Grande" fork_title: "Bifurcar Nova Versão" fork_creating: "A Criar Bifurcação..." generate_terrain: "Gerar Terreno" more: "Mais" wiki: "Wiki" - live_chat: "Chat Ao Vivo" + live_chat: "Chat ao Vivo" + thang_main: "Principal" + thang_spritesheets: "Spritesheets" + thang_colors: "Cores" level_some_options: "Algumas Opções?" level_tab_thangs: "Thangs" level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: level_tab_thangs_all: "Todos" level_tab_thangs_conditions: "Condições Iniciais" level_tab_thangs_add: "Adicionar Thangs" + level_tab_thangs_search: "Pesquisar thangs" + add_components: "Adicionar Componentes" + component_configs: "Configurações dos Componentes" + config_thang: "Clica duas vezes para configurares uma thang" delete: "Eliminar" duplicate: "Duplicar" + stop_duplicate: "Parar de Duplicar" rotate: "Rodar" level_settings_title: "Configurações" level_component_tab_title: "Componentes Atuais" @@ -592,34 +906,37 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: new_level_title_login: "Inicia Sessão para Criares um Novo Nível" new_achievement_title: "Criar uma Nova Conquista" new_achievement_title_login: "Inicia Sessão para Criares uma Nova Conquista" + new_poll_title: "Criar uma Nova Votação" + new_poll_title_login: "Iniciar Sessão para Criar uma Nova Votação" article_search_title: "Procurar Artigos Aqui" thang_search_title: "Procurar Thangs Aqui" level_search_title: "Procurar Níveis Aqui" achievement_search_title: "Procurar Conquistas" + poll_search_title: "Procurar Votações" read_only_warning2: "Nota: não podes guardar nenhuma edição feita aqui, porque não tens sessão iniciada." no_achievements: "Ainda não foram adicionadas conquistas a este nível." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" + achievement_query_misc: "Conquista-chave de uma lista de variados" + achievement_query_goals: "Conquista-chave dos objetivos do nível" level_completion: "Completação do Nível" pop_i18n: "Propagar I18N" + tasks: "Tarefas" + clear_storage: "Limpa as tuas alterações locais" + add_system_title: "Adicionar Sistemas ao Nível" + done_adding: "Adição Concluída" article: edit_btn_preview: "Pré-visualizar" edit_article_title: "Editar Artigo" + polls: + priority: "Prioridade" + contribute: page_title: "Contribuir" - character_classes_title: "Classes das Personagens" - introduction_desc_intro: "Temos esperanças elevadas para o CodeCombat." - introduction_desc_pref: "Queremos ser o sítio onde programadores de todo o tipo vêm para aprender e jogar juntos, introduzir outros ao maravilhoso mundo da programação e retratar as melhores partes da comunidade. Nós não podemos e não queremos fazer isso sozinhos; o que faz de projetos como o GitHub, o Stack Overflow e o Linux ótimos são as pessoas que os usam e constroem neles. Para isso, " - introduction_desc_github_url: "o CodeCombat é totalmente open source" - introduction_desc_suf: " e queremos oferecer tantas maneiras quanto possível para que possas participar e fazer deste projeto tanto teu quanto nosso." - introduction_desc_ending: "Esperamos que te juntes a nós!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy e Matt" + intro_blurb: "O CodeCombat é 100% open source! Centenas de jogadores dedicados ajudaram-nos a transformar o jogo naquilo que ele é hoje. Junta-te a nós e escreve o próximo capítulo da aventura do CodeCombat para ensinar o mundo a programar!" alert_account_message_intro: "Hey, tu!" alert_account_message: "Para te subscreveres para receber e-mails de classes, necessitarás de iniciar sessão." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." + archmage_introduction: "Uma das melhores partes da construção de jogos é que eles sintetizam muitas coisas diferentes. Gráficos, som, rede em tempo real, redes sociais, e, claro, muitos dos aspectos mais comuns da programação, desde a gestão de bases de dados de baixo nível, e administração do servidor até à construção do design e da interface do utilizador. Há muito a fazer, e se és um programador experiente com um verdadeiro desejo de mergulhar nas entranhas do CodeCombat, esta classe pode ser para ti. Gostaríamos muito de ter a tua ajuda para construir o melhor jogo de programação de sempre." class_attributes: "Atributos da Classe" archmage_attribute_1_pref: "Conhecimento em " archmage_attribute_1_suf: ", ou vontade de aprender. A maioria do nosso código está nesta linguagem. Se és um fã de Ruby ou Python, vais sentir-te em casa. É igual ao JavaScript, mas com uma sintaxe melhor." @@ -629,14 +946,11 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: join_desc_2: "para começares, e assinalar a caixa abaixo para te declarares um bravo Arcomago e receberes as últimas notícias por e-mail. Queres falar sobre o que fazer ou como te envolveres mais profundamente no projeto? " join_desc_3: " ou encontra-nos na nossa " join_desc_4: "e começamos a partir daí!" - join_url_email: "Envia-nos um e-mail" + join_url_email: "Contacta-nos" join_url_hipchat: "sala HipChat pública" - more_about_archmage: "Aprende Mais Sobre Tornares-te um Arcomago" archmage_subscribe_desc: "Receber e-mails relativos a novas oportunidades de programação e anúncios." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." -# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" -# artisan_introduction_suf: ", then this class might be for you." + artisan_introduction_pref: "Temos de construir mais níveis! As pessoas estão a pedir mais conteúdo, e nós mesmos só podemos construir estes tantos. Neste momento, a tua estação de trabalho é o nível um; o nosso editor de nível é pouco utilizável, até mesmo pelos seus criadores, por isso fica atento. Se tens visões de campanhas que abranjam 'for-loops' para o" + artisan_introduction_suf: ", então esta classe pode ser para ti." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" # artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." # artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" @@ -645,47 +959,37 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: artisan_join_step2: "Cria um nível novo e explora níveis existentes." artisan_join_step3: "Encontra-nos na nossa sala HipChat pública se necessitares de ajuda." artisan_join_step4: "Coloca os teus níveis no fórum para receberes feedback." - more_about_artisan: "Aprende Mais Sobre Tornares-te um Artesão" artisan_subscribe_desc: "Receber e-mails relativos a novidades do editor de níveis e anúncios." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" - more_about_adventurer: "Aprende Mais Sobre Tornares-te um Aventureiro" adventurer_subscribe_desc: "Receber e-mails quando houver novos níveis para testar." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." + scribe_attribute_1: "Habilidade com palavras é basicamente o que precisas. Não apenas gramática e ortografia, mas seres capaz de explicar ideias complicadas a outros." contact_us_url: "Contacta-nos" - scribe_join_description: "fala-nos um bocado de ti, a tua experiência com a programação e o tipo de coisas sobre o qual gostavas de escrever. Começamos a partir daí!" - more_about_scribe: "Aprende Mais Sobre Tornares-te um Escrivão" + scribe_join_description: "fala-nos um bocado de ti, da tua experiência com a programação e do tipo de coisas sobre as quais gostarias de escrever. Começamos a partir daí!" scribe_subscribe_desc: "Receber e-mails sobre anúncios relativos à escrita de artigos." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." diplomat_introduction_pref: "Portanto, se há uma coisa que aprendemos com o nosso " diplomat_launch_url: "lançamento em Outubro" - diplomat_introduction_suf: "é que há um interesse considerável no CodeCombat noutros países! Estamos a construir um exército de tradutores dispostos a transformar um conjunto de palavras noutro conjuto de palavras, para conseguir que o CodeCombat fique o mais acessível quanto posível em todo o mundo. Se gostas de dar espreitadelas a conteúdos futuros e disponibilizar estes níveis para os teus colegas nacionais o mais depressa possível, então esta classe talvez seja para ti." + diplomat_introduction_suf: "é que há um interesse considerável no CodeCombat noutros países! Estamos a construir um exército de tradutores dispostos a transformar um conjunto de palavras num outro conjuto de palavras, para conseguir que o CodeCombat fique o mais acessível quanto posível em todo o mundo. Se gostas de dar espreitadelas a conteúdos futuros e disponibilizar os níveis para os teus colegas nacionais o mais depressa possível, então esta classe talvez seja para ti." diplomat_attribute_1: "Fluência em Inglês e no idioma para o qual gostarias de traduzir. Quando são tentadas passar ideias complicadas, é importante uma excelente compreensão das duas!" diplomat_i18n_page_prefix: "Podes começar a traduzir os nossos níveis se fores à nossa" diplomat_i18n_page: "página de traduções" diplomat_i18n_page_suffix: ", ou a nossa interface e website no GitHub." diplomat_join_pref_github: "Encontra o ficheiro 'locale' do teu idioma " diplomat_github_url: "no GitHub" - diplomat_join_suf_github: ", edita-o online e submete um 'pull request'. Assinala ainda esta caixa abaixo para ficares atualizado em relação a novos desenvolvimentos da internacionalização!" - more_about_diplomat: "Aprende Mais Sobre Tornares-te um Diplomata" + diplomat_join_suf_github: ", edita-o online e submete um 'pull request'. Assinala ainda a caixa abaixo para ficares atualizado em relação a novos desenvolvimentos da internacionalização!" diplomat_subscribe_desc: "Receber e-mails sobre desenvolvimentos da i18n e níveis para traduzir." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" - more_about_ambassador: "Aprende Mais Sobre Tornares-te um Embaixador" + ambassador_join_note_strong: "Nota" + ambassador_join_note_desc: "Uma das nossas maiores prioridades é construir níveis multijogador onde os jogadores com dificuldade para passar níveis possam invocar feiticeiros mais experientes para os ajudarem. Esta será uma ótima forma de os embaixadores fazerem o que sabem. Vamos manter-te atualizado!" ambassador_subscribe_desc: "Receber e-mails relativos a novidades do suporte e desenvolvimentos do modo multijogador." changes_auto_save: "As alterações são guardadas automaticamente quando clicas nas caixas." diligent_scribes: "Os Nossos Dedicados Escrivões:" @@ -709,7 +1013,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: ratio: "Rácio" leaderboard: "Tabela de Classificação" battle_as: "Lutar como " - summary_your: "As tuas " + summary_your: "As Tuas " summary_matches: "Partidas - " summary_wins: " Vitórias, " summary_losses: " Derrotas" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: fight: "Lutar!" watch_victory: "Vê a tua vitória" defeat_the: "Derrota o" + tournament_started: ", começou" tournament_ends: "O Torneio acaba" tournament_ended: "O Torneio acabou" tournament_rules: "Regras do Torneio" tournament_blurb: "Escreve código, recolhe ouro, constrói exércitos, esmaga inimigos, ganha prémios e melhora a tua carreira no nosso torneio $40,000 Greed! Confere os detalhes" tournament_blurb_criss_cross: "Ganha ofertas, constrói caminhos, supera os adversários, apanha gemas e melhore a tua carreira no nosso torneio Criss-Cross! Confere os detalhes" + tournament_blurb_zero_sum: "Liberta a tua criatividade de programação tanto na recolha de ouro como em táticas de combate nesta batalha-espelhada na montaha, entre o feiticeiro vermelho e o feiticeiro azul. O torneio começou na Sexta-feira, 27 de Março, e decorrerá até às 00:00 de Terça-feira, 7 de Abril. Compete por diversão e glória! Confere os detalhes" tournament_blurb_blog: "no nosso blog" rules: "Regras" winners: "Vencedores" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: no_achievements: "Sem Conquistas ganhas." favorite_prefix: "A linguagem favorita é " favorite_postfix: "." + not_member_of_clans: "Ainda não é membro de nenhum clã." achievements: last_earned: "Último Ganho" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: account: recently_played: "Jogados Recentemente" no_recent_games: "Sem jogos jogados nas passadas duas semanas." + payments: "Pagamentos" + purchased: "Adquirido" + subscription: "Subscrição" + invoices: "Donativos" + service_apple: "Apple" + service_web: "Web" + paid_on: "Pago Em" + service: "Serviço" + price: "Preço" + gems: "Gemas" + active: "Activa" + subscribed: "Subscrito(a)" + unsubscribed: "Não Subscrito(a)" + active_until: "Ativa Até" + cost: "Custo" + next_payment: "Próximo Pagamento" + card: "Cartão" + status_unsubscribed_active: "Não estás subscrito e não te vamos cobrar, mas a tua conta ainda está ativa, por agora." + status_unsubscribed: "Ganha acesso a novos níveis, heróis, itens e gemas de bónus com uma subscrição do CodeCombat!" + + account_invoices: + amount: "Quantidade em dólares americanos" + declined: "O teu cartão foi recusado" + invalid_amount: "Por favor introduz uma quantidade de dólares americanos." + not_logged_in: "Inicia sessão ou cria uma conta para acederes aos donativos." + pay: "Pagar Donativo" + purchasing: "A adquirir..." + retrying: "Erro do servidor, a tentar novamente." + success: "Pago com sucesso. Obrigado!" loading_error: could_not_load: "Erro ao carregar do servidor" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: leaderboard: "Tabela de Classificação" user_schema: "Esquema do Utilizador" user_profile: "Perfil do Utilizador" + patch: "Atualização" patches: "Atualizações" patched_model: "Documento Fonte" model: "Modelo" @@ -838,17 +1175,44 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: user_remarks: "Observações de Utilizador" versions: "Versões" items: "Itens" + hero: "Herói" heroes: "Heróis" - wizard: "Feiticeiro" achievement: "Conquista" clas: "CLAs" play_counts: "Número de Jogos" feedback: "Feedback" + payment_info: "Informações de Pagamento" + campaigns: "Campanhas" + poll: "Votações" + user_polls_record: "Histórico das Votações" + + concepts: + advanced_strings: "'Strings' Avançadas" + algorithms: "Algoritmos" + arguments: "Argumentos" + arithmetic: "Aritmética" + arrays: "'Arrays'" + basic_syntax: "Sintaxe Básica" + boolean_logic: "Lógica 'Boolean'" + break_statements: "Declarações 'Break'" + classes: "Classes" + for_loops: "'For Loops'" + functions: "Funções" + if_statements: "Declarações 'If'" + input_handling: "Manuseamento de 'Input'" + math_operations: "Operações Matemáticas" + object_literals: "'Object Literals'" + strings: "'Strings'" + variables: "Variáveis" + vectors: "Vetores" + while_loops: "'Loops'" + recursion: "Recursão" delta: - added: "Adicionados/as" - modified: "Modificados/as" - deleted: "Eliminados/as" + added: "Adicionado" + modified: "Modificado" +# not_modified: "Not Modified" + deleted: "Eliminado" moved_index: "Índice Movido" text_diff: "Diferença de Texto" merge_conflict_with: "FUNDIR CONFLITO COM" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: legal: page_title: "Legal" - opensource_intro: "O CodeCombat é gratuito para jogar e é totalmente open source." + opensource_intro: "O CodeCombat é completamente open source." opensource_description_prefix: "Confere " github_url: "o nosso GitHub" opensource_description_center: "e ajuda se quiseres! O CodeCombat é construído tendo por base dezenas de projetos open source, os quais nós amamos. Vê " @@ -878,55 +1242,49 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: practices_title: "Melhores Práticas Respeitosas" practices_description: "Estas são as nossas promessas para contigo, o jogador, com um pouco menos de politiquices." privacy_title: "Privacidade" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." + privacy_description: "Nós não vamos vender nenhuma das tuas informações pessoais." security_title: "Segurança" -# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." + security_description: "Nós lutamos para manter as tuas informações pessoais seguras. Sendo um projeto open source, o nosso sítio tem o código disponível, pelo que qualquer pessoa pode rever e melhorar os nossos sistemas de segurança." email_title: "E-mail" email_description_prefix: "Nós não te inundaremos com spam. Através das" email_settings_url: "tuas definições de e-mail" email_description_suffix: "ou através de ligações presentes nos e-mails que enviamos, podes mudar as tuas preferências e parar a tua subscrição facilmente, em qualquer momento." cost_title: "Custo" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" - recruitment_title: "Recrutamento" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." + cost_description: "O CodeCombat é gratuito para os níveis fundamentais, com uma subscrição de $9.99 USD/mês para acederes a ramos de níveis extra e 3500 gemas de bónus por mês. Podes cancelar com um clique, e oferecemos uma garantia de 100% de devolução do dinheiro." copyrights_title: "Direitos Autorais e Licensas" contributor_title: "Contrato de Licença do Contribuinte (CLA)" contributor_description_prefix: "Todas as contribuições, tanto no sítio como no nosso repositório GitHub, estão sujeitas ao nosso" cla_url: "CLA" contributor_description_suffix: "com o qual deves concordar antes de contribuir." code_title: "Código - MIT" -# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" + code_description_prefix: "Todo o código do CodeCombat ou hospedado em codecombat.com, tanto no repositório do GitHub como na base de dados de codecombat.com, está resguardado sob a" mit_license_url: "licença do MIT" -# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." + code_description_suffix: "Isto inclui todo o código dentro dos Sistemas e dos Componentes, o qual é disponibilizado pelo CodeCombat para a criação de níveis." art_title: "Arte/Música - Creative Commons " art_description_prefix: "Todos os conteúdos comuns estão disponíveis à luz da" cc_license_url: "Licença 'Creative Commons Attribution 4.0 International'" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" + art_description_suffix: "Conteúdo comum é, geralmente, qualquer coisa disponibilizada pelo CodeCombat com o propósito de criar Níveis. Isto inclui:" art_music: "Música" art_sound: "Som" art_artwork: "Arte" art_sprites: "Sprites" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." + art_other: "Quaisquer e todos os trabalhos criativos não-código que são disponibilizados aquando da criação de Níveis." # art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." # art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" -# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." + use_list_1: "Se usado num filme ou noutro jogo, inclui 'codecombat.com' nos créditos." # use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." # art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." rights_title: "Direitos Reservados" -# rights_desc: "All rights are reserved for Levels themselves. This includes" + rights_desc: "Todos os direitos estão reservados aos próprios Níveis. Isto inclui" rights_scripts: "Scripts" - rights_unit: "Configuração da unidade" - rights_description: "Descrição" -# rights_writings: "Writings" -# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." + rights_unit: "Configurações das unidades" + rights_description: "Descrições" + rights_writings: "Textos" + rights_media: "Mídia (sons, música) e quaisquer outros conteúdos criativos feitos especificamente para esse Nível e que não foram disponibilizados para a criação de Níveis." # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." -# nutshell_title: "In a Nutshell" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." + nutshell_title: "Resumidamente" + nutshell_description: "Qualquer um dos recursos que fornecemos no Editor de Níveis são de uso livre para criares Níveis. Mas reservamos o direito de distribuição restrita dos próprios Níveis (que são criados em codecombat.com) pelo que podemos cobrar por eles no futuro, se for isso que acabar por acontecer." + canonical: "A versão Inglesa deste documento é a versão definitiva e soberana. Se houver discrepâncias entre traduções, o documento Inglês prevalece." ladder_prizes: title: "Prémios do Torneio" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: license: "licença" oreilly: "ebook à tua escolha" - wizard_settings: - title: "Definições do Feiticeiro" - customize_avatar: "Personaliza o Teu Avatar" - active: "Ativo" - color: "Cor" - group: "Grupo" - clothes: "Roupas" - trim: "Pormenores" - cloud: "Nuvem" - team: "Equipa" - spell: "Feitiço" - boots: "Botas" - hue: "Tom" - saturation: "Saturação" - lightness: "Brilho" - account_profile: settings: "Definições" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Editar Perfil" @@ -1050,23 +1392,23 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # education_degree: "Degree" # education_degree_help: "What was your degree and field of study?" # education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" + education_duration_help: "Quando?" + education_description: "Descrição" + education_description_help: "Destaca algo sobre esta experiência educacional. (140 carateres; opcional)" # our_notes: "CodeCombat's Notes" # remarks: "Remarks" projects: "Projetos" projects_header: "Adiciona 3 projetos" -# projects_header_2: "Projects (Top 3)" + projects_header_2: "Projetos (Top 3)" # projects_blurb: "Highlight your projects to amaze employers." project_name: "Nome do Projeto" -# project_name_help: "What was the project called?" + project_name_help: "Qual era o nome do projeto?" project_description: "Descrição" project_description_help: "Descreve o projeto em poucas palavras." project_picture: "Imagem" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." + project_picture_help: "Faz upload de uma imagem 230x115px ou maior que mostre o projeto." project_link: "Ligação" -# project_link_help: "Link to the project." + project_link_help: "Ligação para o projeto." player_code: "Código do Jogador" employers: diff --git a/app/locale/ro.coffee b/app/locale/ro.coffee index 034341a82..8f4c4a4da 100644 --- a/app/locale/ro.coffee +++ b/app/locale/ro.coffee @@ -1,16 +1,17 @@ module.exports = nativeDescription: "limba română", englishDescription: "Romanian", translation: home: - slogan: "Învață sa scrii cod jucându-te" + slogan: "Învață să scrii cod jucându-te" no_ie: "CodeCombat nu merge pe Internet Explorer 8 sau mai vechi. Scuze!" # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat nu a fost proiectat pentru dispozitive mobile si s-ar putea sa nu meargă!" # Warning that shows up on mobile devices - play: "Joacă" # The big play button that just starts playing a level + no_mobile: "CodeCombat nu a fost proiectat pentru dispozitive mobile şi s-ar putea să nu meargă!" # Warning that shows up on mobile devices + play: "Joacă" # The big play button that opens up the campaign view. old_browser: "Mda , browser-ul tău este prea vechi pentru CodeCombat. Scuze!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Poți să încerci oricum ,dar probabil nu o să meargă." + ipad_browser: "Veşti rele: CodeCombat nu rulează pe iPad in browser. Veşti bune: aplicaţia noastră nativă pentru iPad e în curs de aprobare." campaign: "Campanie" for_beginners: "Pentru Începători" multiplayer: "Multiplayer" # Not currently shown on home page - for_developers: "Pentru dezvoltatori" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + for_developers: "Pentru Dezvoltatori" # Not currently shown on home page. + or_ipad: "Sau descarcă pentru iPad" nav: play: "Nivele" # The top nav bar entry where players choose which levels to play @@ -29,7 +30,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman about: "Despre" contact: "Contact" twitter_follow: "Urmărește" -# teachers: "Teachers" + teachers: "Educatori" modal: close: "Inchide" @@ -51,112 +52,130 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman spectate: "Spectator" # Ladder page players: "jucători" # Hover over a level on /play hours_played: "ore jucate" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + items: "Iteme" # Tooltip on item shop button from /play + unlock: "Deblochează" # For purchasing items and heroes + confirm: "Confirmă" + owned: "Deținute" # For items you own + locked: "Blocate" + purchasable: "De cumpărat" # For a hero you unlocked but haven't purchased + available: "Valabile" + skills_granted: "Skill-uri acordate" # Property documentation details + heroes: "Eroi" # Tooltip on hero shop button from /play + achievements: "Realizări" # Tooltip on achievement list button from /play + account: "Cont" # Tooltip on account button from /play + settings: "Setări" # Tooltip on settings button from /play + poll: "Sondaj" # Tooltip on poll button from /play + next: "Următorul" # Go from choose hero to choose inventory before playing a level + change_hero: "Schimbă eroul" # Go back from choose inventory to choose hero + choose_inventory: "Foloseste Itemele" + buy_gems: "Cumpără Pietre Prețioase" + subscription_required: "Abonament Necesar" + anonymous: "Jucător Anonim" level_difficulty: "Dificultate: " campaign_beginner: "Campanie pentru Începători" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Alege nivelul" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Poți să sari la orice nivel de mai jos" - adventurer_forum: "forumul Aventurierului" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... în care se învață tainele programării." - campaign_dev: "Nivele aleatoare mai grele" - campaign_dev_description: "... în care se învață interfața, cu o dificultate puțin mai mare." + awaiting_levels_adventurer_prefix: "Lansăm niveluri noi în fiecare săptămână." + awaiting_levels_adventurer: "Înscrie-te ca un aventurier " + awaiting_levels_adventurer_suffix: "pentru a fi primul care joacă nivele noi." + adjust_volume: "Reglează volumul" campaign_multiplayer: "Arene Multiplayer" - campaign_multiplayer_description: "... în care te lupți cap-la-cap contra alti jucători." - campaign_player_created: "Create de jucători" - campaign_player_created_description: "... în care ai ocazia să testezi creativitatea colegilor tai <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" + campaign_multiplayer_description: "... în care te lupți cap-la-cap contra alți jucători." +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Faci progrese mari! Spune-le părinților cât de mult ai învățat cu CodeCombat." + email_invalid: "Adresă Email invalidă." + form_blurb: "Introduceți adresa e-mail al unui părinte mai jos și le vom arăta!" + form_label: "Adresă Email" + placeholder: "adresă email" + title: "Excelentă treabă, Ucenicule" login: sign_up: "Crează cont" log_in: "Log In" logging_in: "Se conectează" log_out: "Log Out" - recover: "recuperează cont" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Ai uitat parola?" + authenticate_gplus: "Autentificare G+" + load_profile: "Încarca cont G+" + finishing: "Terminare" + sign_in_with_facebook: "Conectați-vă cu Facebook" + sign_in_with_gplus: "Conectați-vă cu G+" + signup_switch: "Doriți să creați un cont?" signup: - create_account_title: "Crează cont pentru a salva progresul" - description: "Este gratis. Doar un scurt formular inainte si poți continua:" email_announcements: "Primește notificări prin email" - coppa: "13+ sau non-USA " - coppa_why: "(De ce?)" creating: "Se creează contul..." sign_up: "Înscrie-te" log_in: "loghează-te cu parola" social_signup: "Sau, te poți inregistra cu Facebook sau G+:" required: "Trebuie să te înregistrezi înaite să parcurgi acest drum." + login_switch: "Ai deja un cont?" recover: recover_account_title: "Recuperează Cont" send_password: "Trimite parolă de recuperare" -# recovery_sent: "Recovery email sent." + recovery_sent: "Email de recuperare trimis." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Primar" + secondary: "Secundar" + armor: "Armură" + accessories: "Accesorii" + misc: "Diverse" + books: "Cărti" common: + back: "Inapoi" # When used as an action verb, like "Navigate backward" + continue: "Continuă" # When used as an action verb, like "Continue forward" loading: "Se incarcă..." saving: "Se salvează..." sending: "Se trimite..." send: "Trimite" cancel: "Anulează" save: "Salvează" - publish: "Publica" + publish: "Publică" create: "Creează" manual: "Manual" fork: "Fork" play: "Joacă" # When used as an action verb, like "Play next level" retry: "Reîncearca" -# watch: "Watch" -# unwatch: "Unwatch" + actions: "Acţiuni" + info: "Info" + help: "Ajutor" + watch: "Watch" + unwatch: "Unwatch" submit_patch: "Înainteaza Patch" + submit_changes: "Trimite modificări" + save_changes: "Salveaza modificări" general: and: "și" name: "Nume" -# date: "Date" + date: "Dată" body: "Corp" version: "Versiune" + pending: "În așteptare" + accepted: "Acceptat" + rejected: "Respins" + withdrawn: "Retrage" + submitter: "Expeditor" + submitted: "Expediat" commit_msg: "Înregistrează Mesajul" -# version_history: "Version History" - version_history_for: "Versiune istorie pentru: " + review: "Revizuie" + version_history: "Istoricul versiunilor" + version_history_for: "Istoricul versiunilor pentru: " + select_changes: "Selectați două schimbări de mai jos pentru a vedea diferenţa." + undo_prefix: "Undo" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Redo" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Joaca previzualizarea nivelului actual" result: "Rezultat" - results: "Resultate" + results: "Rezultate" description: "Descriere" or: "sau" -# subject: "Subject" + subject: "Subiect" email: "Email" password: "Parolă" message: "Mesaj" @@ -172,8 +191,11 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman easy: "Ușor" medium: "Mediu" hard: "Greu" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player: "Jucător" + player_level: "Nivel" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Războinic" + ranger: "Arcaș" + wizard: "Vrăjitor" units: second: "secundă" @@ -186,7 +208,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman days: "zile" week: "săptămână" weeks: "săptămâni" - month: "luna" + month: "lună" months: "luni" year: "an" years: "ani" @@ -194,76 +216,84 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman play_level: done: "Gata" home: "Acasă" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" + level: "Nivel" # Like "Level: Dungeons of Kithgard" + skip: "Sari peste" game_menu: "Meniul Jocului" guide: "Ghid" restart: "Restart" goals: "Obiective" -# goal: "Goal" -# running: "Running..." + goal: "Obiectiv" + running: "Rulează..." success: "Success!" incomplete: "Incomplet" - timed_out: "Ai ramas fara timp" + timed_out: "Ai rămas fără timp" failing: "Eşec" action_timeline: "Timeline-ul acțiunii" click_to_select: "Apasă pe o unitate pentru a o selecta." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "Multiplayer" + control_bar_join_game: "Alătură-Te Jocului" + reload: "Reîncarca" reload_title: "Reîncarcă tot codul?" reload_really: "Ești sigur că vrei să reîncarci nivelul de la început?" - reload_confirm: "Reload All" + reload_confirm: "Reîncarca Tot" + victory: "Victorie" victory_title_prefix: "" victory_title_suffix: " Terminat" victory_sign_up: "Înscrie-te pentru a salva progresul" victory_sign_up_poke: "Vrei să-ți salvezi codul? Crează un cont gratis!" victory_rate_the_level: "Apreciază nivelul: " # Only in old-style levels. victory_return_to_ladder: "Înapoi la jocurile de clasament" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Joacă nivelul următor" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "Continuă" + victory_saving_progress: "Salvează Progresul" victory_go_home: "Acasă" # Only in old-style levels. victory_review: "Spune-ne mai multe!" # Only in old-style levels. victory_hour_of_code_done: "Ai terminat?" victory_hour_of_code_done_yes: "Da, am terminat Hour of Code™!" + victory_experience_gained: "Ai câștigat XP" + victory_gems_gained: "Ai câștigat Pietre Prețioase" + victory_new_item: "Item nou" + victory_viking_code_school: "Wow, ăla a fost un nivel greu! Daca nu ești deja un dezvoltator de software, ar trebui să fi. Tocmai ai fost selectat pentru acceptare in Viking Code School, unde poți sa iți dezvolți abilitățile la nivelul următor și să devi un dezvoltator web profesionist în 14 săptămâni." + victory_become_a_viking: "Devino Viking" guide_title: "Ghid" - tome_minion_spells: "Vrăjile Minion-ilor tăi" # Only in old-style levels. + tome_minion_spells: "Vrăjile Minionilor tăi" # Only in old-style levels. tome_read_only_spells: "Vrăji Read-Only" # Only in old-style levels. tome_other_units: "Alte unități" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_run: "Ruleaza" + tome_cast_button_running: "In Derulare" + tome_cast_button_ran: "A rulat" + tome_submit_button: "Trimite" + tome_reload_method: "Reîncarcă cod original, pentru această metodă" # Title text for individual method reload button. + tome_select_method: "Selectați o metodă" + tome_see_all_methods: "Vezi toate metodele pe care le poți edita" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Alege pe cineva pentru " - tome_available_spells: "Vrăjile disponibile" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_available_spells: "Vrăji disponibile" + tome_your_skills: "Skillurile tale" + tome_help: "Ajutor" + tome_current_method: "Metoda curentă" + hud_continue_short: "Continuă" + code_saved: "Cod Salvat" skip_tutorial: "Sari peste (esc)" keyboard_shortcuts: "Scurtături Keyboard" loading_ready: "Gata!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" + loading_start: "Începe Level" + problem_alert_title: "Repară codul" + problem_alert_help: "Ajutor" + time_current: "Acum:" + time_total: "Max:" + time_goto: "Dute la:" + non_user_code_problem_title: "Imposibil de încărcat Nivel" + infinite_loop_title: "Buclă infinită detectată" + infinite_loop_description: "Codul initial pentru a construi lumea nu a terminat de rulat. Este, probabil, foarte lent sau are o buclă infinită. Sau ar putea fi un bug. Puteți încerca acest cod nou sau resetați codul la starea implicită. Dacă nu-l repara, vă rugăm să ne anunțați." + check_dev_console: "Puteți deschide, de asemenea, consola de dezvoltator pentru a vedea ce ar putea merge gresit." + check_dev_console_link: "(instrucțiuni)" + infinite_loop_try_again: "Încearcă din nou" + infinite_loop_reset_level: "Resetează Nivelul" + infinite_loop_comment_out: "Comentează Codul" tip_toggle_play: "Pune sau scoate pauza cu Ctrl+P." - tip_scrub_shortcut: "Înapoi și derulare rapidă cu Ctrl+[ and Ctrl+]." + tip_scrub_shortcut: "Înapoi și derulare rapidă cu Ctrl+[ and Ctrl+]." # {change} tip_guide_exists: "Apasă pe ghidul din partea de sus a pagini pentru informații utile." tip_open_source: "CodeCombat este 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat a fost lansat beta in Octombrie 2013." tip_think_solution: "Gândește-te la soluție, nu la problemă." tip_theory_practice: "Teoretic nu este nici o diferență înte teorie și practică. Dar practic este. - Yogi Berra" @@ -273,124 +303,244 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman tip_baby_coders: "În vitor până și bebelușii vor fi Archmage." tip_morale_improves: "Se va încărca până până când va crește moralul." tip_all_species: "Noi credem în șanse egale de a învăța programare pentru toate speciile." - tip_reticulating: "Reticulating spines." + tip_reticulating: "Reticulăm coloane vertebrale." tip_harry: "Ha un Wizard, " - tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" - tip_patience: "Să ai rabdare trebuie, tinere Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." -# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Personalizează Wizard-ul" + tip_great_responsibility: "Cu o mare abilitate mare de programare vine o mare responsabilitate de debugging." + tip_munchkin: "Daca nu iți mananci legumele, un munchkin va veni după tine cand dormi." + tip_binary: "Sunt doar 10 tipuri de oameni in lume: cei ce îteleg sistemul binar, si ceilalți." + tip_commitment_yoda: "Un programator trebuie să aiba cel mai profund angajament, si mintea cea mai serioasă. ~Yoda" + tip_no_try: "Fă. Sau nu mai face. Nu exista voi încerca. ~Yoda" + tip_patience: "Să ai rabdare trebuie, tinere Padawan. ~Yoda" + tip_documented_bug: "Un bug documentat nu e chiar un bug; este o caracteristica." + tip_impossible: "Mereu pare imposibil până e gata. ~Nelson Mandela" + tip_talk_is_cheap: "Vorbele sunt ieftine. Arată-mi codul. ~Linus Torvalds" + tip_first_language: "Cel mai dezastruos lucru pe care poți să îl înveți este primul limbaj de programare. ~Alan Kay" + tip_hardware_problem: "Î: De cați programatori ai nevoie ca să schimbi un bec? R: Niciunul, e o problemă hardware." + tip_hofstadters_law: "Legea lui Hofstadter: Mereu dureaza mai mult decât te aștepți, chiar dacă iei în considerare Legea lui Hofstadter." + tip_premature_optimization: "Optimizarea prematură este rădăcina tuturor răutăților. ~Donald Knuth" + tip_brute_force: "Atunci cănd ești în dubii, folosește brute force. ~Ken Thompson" + tip_extrapolation: "Există două feluri de oameni: cei care pot extrapola din date incomplete..." + tip_superpower: "Programarea este cel mai apropiat lucru de o superputere." + tip_control_destiny: "In open source, ai dreptul de a-ți controla propiul destin. ~Linus Torvalds" + tip_no_code: "Nici-un cod nu e mai rapid decat niciun cod." + tip_code_never_lies: "Codul nu minte niciodată, commenturile mai mint. ~Ron Jeffries" + tip_reusable_software: "Înainte ca un software să fie reutilizabil, trebuie să fie mai întâi utilizabil." + tip_optimization_operator: "Fiecare limbaj are un operator de optimizare. La majoritatea acela este '//'" + tip_lines_of_code: "Măsurarea progresului în lini de cod este ca și măsurarea progresului de construcție a aeronavelor în greutate. ~Bill Gates" + tip_source_code: "Vreau să schimb lumea dar nu îmi dă nimeni codul sursă." + tip_javascript_java: "Java e pentru JavaScript exact ce e o Mașina pentru o Carpetă. ~Chris Heilmann" + tip_move_forward: "Orice ai face, dăi înainte. ~Martin Luther King Jr." + tip_google: "Ai o problemă care nu o poți rezolva? Folosește Google!" + tip_adding_evil: "Adaugăm un strop de răutate." + tip_hate_computers: "Tocmai aia e problema celor ce urăsc calulatoarele, ei defapt urăsc programatorii nepricepuți. ~Larry Niven" + tip_open_source_contribute: "Poți ajuta la îmbunătățirea jocului CodeCombat!" + tip_recurse: "A itera este uman, recursiv este divin. ~L. Peter Deutsch" + tip_free_your_mind: "Trebuie sa lași totul, Neo. Frica, Îndoiala și necredința. Eliberează-ți mintea. ~Morpheus" + tip_strong_opponents: "Și cei mai puternici dintre oponenți întodeauna au o slăbiciune. ~Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "Inventar" + save_load_tab: "Salvează/Încarcă" + options_tab: "Opțiuni" + guide_tab: "Ghid" + guide_video_tutorial: "Tutorial Video" + guide_tips: "Sfaturi" multiplayer_tab: "Multiplayer" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" + auth_tab: "Înscriete" + inventory_caption: "Echipeazăți Eroul" + choose_hero_caption: "Alege Eroul, limbajul" + save_load_caption: "... și vezi istoricul" options_caption: "Configurarea setărilor" guide_caption: "Documentație si sfaturi" multiplayer_caption: "Joaca cu prieteni!" -# auth_caption: "Save your progress." + auth_caption: "Salvează progresul." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Tabel Clasificare" + view_other_solutions: "Vizualizează Tabelul de Clasificare" + scores: "Scoruri" + top_players: "Top Jucători" + day: "Astăzi" + week: "Săptămâna Aceasta" + all: "Tot Timpul" + time: "Timp" + damage_taken: "Damage Primit" + damage_dealt: "Damage Oferit" + difficulty: "Dificultate" + gold_collected: "Aur Colectat" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "Echipează Iteme" + equipped_item: "Echipat" + required_purchase_title: "Necesar" + available_item: "Valabil" + restricted_title: "Restricționat" + should_equip: "(dublu-click pentru echipare)" + equipped: "(echipat)" + locked: "(blocat)" + restricted: "(în acest nivel)" + equip: "Echipează" + unequip: "Dezechipează" + + buy_gems: + few_gems: "Căteva Pietre Prețioase" + pile_gems: "Un morman de Pietre Prețioase" + chest_gems: "Un cufăr de Pietre Prețioase" + purchasing: "Cumpărare..." + declined: "Cardul tău a fost refuzat." + retrying: "Eroare de server, reîncerc." + prompt_title: "Nu sunt destule Pietre Prețioase." + prompt_body: "Vrei mai multe?" + prompt_button: "Intră în magazin." + recovered: "Pietre Prețioase cumpărate anterior recuperate.Va rugăm să dați refresh la pagină." + price: "x3500 / mo" + + subscribe: + comparison_blurb: "Îmbunătățeșteți abilitățile cu abonamentul CodeCombat" + feature1: "80+ de nivele de bază în 4 lumi diferite!" # {change} + feature2: "7 <strong>Eroi Noi</strong> puternici, cu skilluri unice!" # {change} + feature3: "60+ nivele bonus" # {change} + feature4: "<strong>3500 de Pietre Prețioase bonus</strong> în fiecare lună!" + feature5: "Tutoriale Video" + feature6: "Suport e-mail premium" + feature7: "<strong>Clanuri</strong> private" + free: "Gratuit" + month: "lună" + subscribe_title: "Abonează-te" + unsubscribe: "Dezabonează-te" + confirm_unsubscribe: "Confirmă Dezabonarea" + never_mind: "Nu contează, eu tot te iubesc!" + thank_you_months_prefix: "Mulțumesc pentru sprijinul acordat în aceste" + thank_you_months_suffix: "luni." + thank_you: "Mulțumim pentru susținerea jocului CodeCombat!" + sorry_to_see_you_go: "Ne pare rău ca pleci! Te rugăm să ne spui ce am fi putut face mai bine." + unsubscribe_feedback_placeholder: "O, ce am făcut?" + parent_button: "Întreabă un părinte" + parent_email_description: "Le vom trimite un email ca să vă cumpere un abonament CodeCombat" + parent_email_input_invalid: "Adresă e-mail invalidă" + parent_email_input_label: "Adresa e-mail a unui părinte" + parent_email_input_placeholder: "Introduceți e-mail părinte" + parent_email_send: "Trimite Email" + parent_email_sent: "Email trimis!" + parent_email_title: "Care este e-mailul părintelui tău?" + parents: "Pentru Părinți" + parents_title: "Dragă Părinte: Copilul tău învață programare. Vrei să îl ajuți să continue?" + parents_blurb1: "Copilul tău a jucat __nLevels__ nivele și a învățat bazele programări. Ajutăl și cultivă interesul cumpărând un abonament ca să joace în continuare." + parents_blurb1a: "Programarea unui computer este o abilitate esențială pe care copilul dumneavoastră o va folosi fără îndoială ca adult. Până in 2020, va fi nevoie de abilități de software de bază de 77% din locurile de muncă, și ingineri software sunt în cerere mare în întreaga lume. Știați că Informatica este gradul universitar cel mai bine plătit?" + parents_blurb2: "Pentru $9.99 USD/lună, copilul tău va primi provocări noi în fiecare săptămână și ajutor personal pe e-mail de la programatori profesioniști." + parents_blurb3: "Fără Riscuri: Garanție 100% bani înapoi, o dezabonare ușoară cu 1-click." + payment_methods: "Metode de plată" + payment_methods_title: "Metode de plată acceptate" + payment_methods_blurb1: "Deocamdată acceptăm numai cărti de credit si Alipay." + payment_methods_blurb2: "Dacă aveți nevoie de o formă alternativă de plată, vă rugăm să ne contactați" + stripe_description: "Abonament Lunar" + subscription_required_to_play: "Ai nevoie de abonament ca să joci acest nivel." + unlock_help_videos: "Abonează-te pentru deblocarea tuturor tutorialelor video." + personal_sub: "Abonament Personal" # Accounts Subscription View below + loading_info: "Se încarcă informațile despre abonament..." + managed_by: "Gestionat de" + will_be_cancelled: "Va fi anulat pe" + currently_free: "Ai un abonament gratuit" + currently_free_until: "Ai un abonament gratuit până pe" + was_free_until: "Ai avut un abonament gratuit până pe" + managed_subs: "Abonamente Gestionate" + managed_subs_desc: "Adaugă abonamente pentru alți jucători (studenți, copii, etc.)" + managed_subs_desc_2: "Recipienți trebuie să aibă un cont CodeCombat associat cu adresa e-mail oferită." + group_discounts: "Discount pentru Grupuri" + group_discounts_1: "De asemenea, oferim reduceri de grup pentru abonamente în vrac." + group_discounts_1st: "Primul Abonament" + group_discounts_full: "Preț Intreg" + group_discounts_2nd: "2-11 abonamente" + group_discounts_20: "20% reducere" + group_discounts_12th: "12+ abonamente" + group_discounts_40: "40% reducere" + subscribing: "Te abonăm..." + recipient_emails_placeholder: "Introduceți adresa de email ca să vă abonăm, una pe fiecare linie." + subscribe_users: "Abonați Userii" + users_subscribed: "Useri abonați:" + no_users_subscribed: "Nici-un user abonat, verificațî adresele e-mail." + current_recipients: "Recipienți curenți" + unsubscribing: "Te Dezabonăm..." + subscribe_prepaid: "Dăi Click pe Abonare pentru a folosi un cod prepaid" + using_prepaid: "Folosesc codul prepaid pentru un abonament lunar" choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" + choose_hero: "Alege Eroul" + programming_language: "Limbaj de Programare" + programming_language_description: "Ce limbaj de programare vrei să folosești?" + default: "Implicit" + experimental: "Experimental" python_blurb: "Simplu dar puternic, Python este un limbaj de uz general extraordinar!" javascript_blurb: "Limbajul web-ului." - coffeescript_blurb: "JavaScript cu o syntaxă mai placută! Nicer JavaScript syntax." + coffeescript_blurb: "JavaScript cu o syntaxă mai placută!" clojure_blurb: "Un Lisp modern." lua_blurb: "Limbaj de scripting pentru jocuri." io_blurb: "Simplu dar obscur." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + status: "Stare" + hero_type: "Tip" + weapons: "Armament" + weapons_warrior: "Săbii - Distanță Scurtă, Fără Magie" + weapons_ranger: "Arbalete, Arme - Distanță Mare, Fără Magie" + weapons_wizard: "Baghete, Toiage, - Distanță Mare, Și Magie" + attack: "Attack" # Can also translate as "Attack" + health: "Viață" + speed: "Viteză" + regeneration: "Regenerare" + range: "Rază" # As in "attack or visual range" + blocks: "Blochează" # As in "this shield blocks this much damage" + backstab: "Înjunghiere" # As in "this dagger does this much backstab damage" + skills: "Skilluri" + attack_1: "Oferă" + attack_2: "" + attack_3: "Damage cu arma." + health_1: "Primește" + health_2: "" + health_3: "Armură." + speed_1: "Se mișcă cu" + speed_2: "metri pe secundă." + available_for_purchase: "Disponibil pentru cumpărare" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Pentru deblocare termină nivelul:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Numai anumiți eroi pot juca acest nivel" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + skill_docs: + writable: "permisiuni de scriere" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "permisiuni doar de citire" + action_name: "nume" + action_cooldown: "Ține" + action_specific_cooldown: "Cooldown" + action_damage: "Damage" + action_range: "Rază de acțiune" + action_radius: "Rază" + action_duration: "Durată" + example: "Exemplu" + ex: "ex" # Abbreviation of "example" + current_value: "Valoare Curentă" + default_value: "Valoare Implicită" + parameters: "Parametrii" + returns: "Întoarce" + granted_by: "Acordat de" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + save_load: + granularity_saved_games: "Salvate" + granularity_change_history: "Istoric" options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." + general_options: "Opțiuni Generale" # Check out the Options tab in the Game Menu while playing a level + volume_label: "Volum" + music_label: "Muzică" + music_description: "Oprește Muzica din fundal." editor_config: "Editor Config" editor_config_title: "Configurare Editor" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." + editor_config_level_language_label: "Limbajul pentru acest nivel" + editor_config_level_language_description: "Definește limbajul de programare pentru acest nivel." + editor_config_default_language_label: "Limbajul de Programare Implicit" + editor_config_default_language_description: "Definește limbajul de programare în care vrei să scri codul când începi un nivel nou." editor_config_keybindings_label: "Mapare taste" - editor_config_keybindings_default: "Default (Ace)" - editor_config_keybindings_description: "Adaugă comenzi rapide suplimentare cunoscute din editoarele obisnuite." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." + editor_config_keybindings_default: "Implicit (Ace)" + editor_config_keybindings_description: "Adaugă comenzi rapide suplimentare, cunoscute din editoarele obisnuite." + editor_config_livecompletion_label: "Autocompletare Live" + editor_config_livecompletion_description: "Afișează sugesti de autocompletare în timp ce scri." editor_config_invisibles_label: "Arată etichetele invizibile" editor_config_invisibles_description: "Arată spațiile și taburile invizibile." editor_config_indentguides_label: "Arată ghidul de indentare" @@ -405,41 +555,135 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman why_paragraph_2_italic: "wow o insignă" why_paragraph_2_center: "ci" why_paragraph_2_italic_caps: "TREBUIE SĂ TERMIN ACEST NIVEL!" - why_paragraph_2_suffix: "De aceea CodeCombat este un joc multiplayer, nu un curs transfigurat în joc. Nu ne vom opri până când tu nu te poți opri--și de data asta, e de bine." + why_paragraph_2_suffix: "De aceea CodeCombat este un joc multiplayer, nu un curs transfigurat în joc. Nu ne vom opri până când tu nu te mai poți opri--dar de data asta, e de bine." why_paragraph_3: "Dacă e să devi dependent de vreun joc, devino dependent de acesta și fi un vrăjitor al noii ere tehnologice." -# press_title: "Bloggers/Press" -# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" -# press_paragraph_1_link: "press packet" -# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" + press_title: "Bloggeri/Presă" + press_paragraph_1_prefix: "Vrei să scri despre noi? Poți să folosești toate resursele incluse în:" + press_paragraph_1_link: "pachetul pentru presă" + press_paragraph_1_suffix: ". Toate logourile si imaginile pot fi folosite fără să ne contactezi direct." + team: "Echipa" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat: Info pentru Educatori" + intro_1: "CodeCombat este un joc online care te învață programare. Studenți scriu cod în limbaje de programare reale." + intro_2: "Nu este nevoie de experiența în domeniu!" + free_title: "Cât de mult costă?" + cost_china: "CodeCombat în China este gratis pentru primele 5 nivele, după care costă $9.99 USD pe lună pentru a accesa celelalte 140+ nivele pe serverele noaste exlusive din China." + free_1: "Sunt 80+ de nivele GRATIS ce parcurg fiecare concept în programare." # {change} + free_2: "Un abonament lunar oferă acces la tutoriale video si nivele extra pentru practică." + teacher_subs_title: "Educatori primesc abonamente gratuite!" + teacher_subs_1: "Te rugăm sa completezi" + teacher_subs_2: "Chestionarul Educatorului" + teacher_subs_3: "pentru a configura abonamentul." + sub_includes_title: "Ce e inclus în abonament?" + sub_includes_1: "Pe langă 80+ de nivele de bază, studenții cu un abonament lunar au acces la aceste servicii suplimentare:" # {change} + sub_includes_2: "60+ nivele pentru practică" # {change} + sub_includes_3: "Tutoriale Video" + sub_includes_4: "Suport pe e-mail premium" + sub_includes_5: "7 eroi noi cu abilități unice de stăpânit" # {change} + sub_includes_6: "3500 de Pietre Prețioase bonus in fiecare lună" + sub_includes_7: "Clanuri Private" + monitor_progress_title: "Cum monitorizez progresul unui student?" + monitor_progress_1: "Progresul poate fi urmărit creând" + monitor_progress_2: "pentru clasa ta." + monitor_progress_3: "Pentru a adăuga un student, trimite o invitație în Clanul tău, care este pe" + monitor_progress_4: "pagină." + monitor_progress_5: "După ce se alătură, vei vedea un rezumat cu progresul studentului pe pagina Clanului tău." + private_clans_1: "Clanurile Private oferă intimitate si informați despre progres detaliate pentru fiecare student." + private_clans_2: "Pentru a crea un Clan privat, verifică caseta \"Fă Clanul Privat\" când creezi un" + private_clans_3: "." + who_for_title: "Pentru cine este CodeCombat?" + who_for_1: "Recomandăm CodeCombat pentru studenți cu vârsta de la 9 ani în sus. Nu este nevoie de experiență de programare." + who_for_2: "Am proiectat CodeCombat pentru a atrage atât băieți cat si fete." + material_title: "Cât de mult material conține?" + material_china: "Aproximativ 30 de ore de gameplay răspândite peste 140+ de nivele pentru abonați, si nivele noi în fiecare săptămână." # {change} + material_1: "Aproximativ 10 ore de material gratis si adițional 20 de ore în plus pentru abonați, cu nivele noi în fiecare săptămână." # {change} + concepts_title: "Ce concepte sunt parcurse?" + how_much_title: "Cât de mult costă un abonament lunar?" + how_much_1: "Un" + how_much_2: "Abonament Lunar" + how_much_3: "costă $9.99, și poate fi anulat oricând." + how_much_4: "Adițional, oferim discount-uri pentru grupui mari:" + how_much_5: "Acceptăm achiziții unice, si abonamente anuale pentru grupuri, cum ar fi o clasă de elevi, toate cu discount. Contactați-ne" + how_much_6: "pentru mai multe detalii." + more_info_title: "Unde găsesc mai multe informații?" + more_info_1: "" + more_info_2: "Forumul de educatori" + more_info_3: "este un loc bun pentru a vă conecta cu colegii educatori ce folosesc CodeCombat." + sys_requirements_title: "Cerințe de Sistem" + sys_requirements_1: "Un browser modern. Versiuni mai noi de Chrome, Firefox, sau Safari. Internet Explorer 9 sau o versiune mai nouă." + sys_requirements_2: "CodeCombat nu funcționează pe iPad încă" + + teachers_survey: + title: "Chestionar Educatori" + must_be_logged: "Mai întâi trebuie să fi logat. Te rugăm să faci un cont sau să te loghezi în meniul de mai sus." + retrieving: "Se preiau informațiile..." + being_reviewed_1: "Cererea pentru un abonament trial gratis este în curs de" + being_reviewed_2: "examinare" # {change} + approved_1: "Cererea pentru un abonament trial gratis a fost" + approved_2: "aprobată." # {change} + approved_3: "Mai multe instrucțiuni au fost timiste la" + denied_1: "Cererea pentru un abonament trial gratis a fost" + denied_2: "refuzată" # {change} + contact_1: "Contactează-ne" + contact_2: "dacă ai alte întrebări." + description_1: "Oferim abonamente gratis Educatorilor pentru evaluare. Poți găsi mai multe informați pe" + description_2: "pagina" + description_3: "educatorilor." + description_4: "Te rugăm să completezi acest formular rapid si îți vom trimite un email cu instrucțiuni." + email: "Adresă Email" +# school: "Name of School" +# location: "Name of City" + age_students: "Ce vârstă au studenții tăi?" + under: "Sub" + other: "Altele:" + amount_students: "La câți elevi le predai?" + hear_about: "De unde ai auzit de CodeCombat?" + fill_fields: "Te rugăm să completezi toate câmpurile." + thanks: "Mulțumim! Vom trimite instrucțiunile de setup în scurt timp." versions: save_version_title: "Salvează noua versiune" new_major_version: "Versiune nouă majoră" + submitting_patch: "Trimitere Patch..." cla_prefix: "Pentru a salva modificările mai intâi trebuie sa fiți de acord cu" cla_url: "CLA" cla_suffix: "." cla_agree: "SUNT DE ACORD" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Contact CodeCombat" welcome: "Folosiți acest formular pentru a ne trimite email. " - contribute_prefix: "Dacă sunteți interesați in a contribui uitați-vă pe " - contribute_page: "pagina de contribuție" - contribute_suffix: "!" forum_prefix: "Pentru orice altceva vă rugăm sa incercați " forum_page: "forumul nostru" forum_suffix: " în schimb." + faq_prefix: "Există si un" + faq: "FAQ" + subscribe_prefix: "Daca ai nevoie de ajutor ca să termini un nivel te rugăm să" + subscribe: "cumperi un abonament CodeCombat" + subscribe_suffix: "si vom fi bucuroși să te ajutăm cu codul." + subscriber_support: "Din moment ce ești un abonat CodeCombat, adresa ta de email va primi sprijinul nostru prioritar." + screenshot_included: "Screenshot-uri incluse." + where_reply: "Unde ar trebui să răspundem?" send: "Trimite Feedback" contact_candidate: "Contacteaza Candidatul" # Deprecated recruitment_reminder: "Folosiți acest formular pentru a ajunge la candidații care va intereseaza pentru interviu. CodeCombat percepe 15% din salariu în primul an. Taxa este datorată la angajare și este rambursabilă pentru 90 de zile în cazul în care salariatul nu rămâne angajat. Cele part time, și angajați cu contract la distanță sunt gratuite, așa cum sunt stagiari." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman autosave: "Modificările se salvează automat" me_tab: "Eu" picture_tab: "Poză" - upload_picture: "Uploadeaza o imagine" + delete_account_tab: "Șterge Contul" + wrong_email: "Email Greșit" +# wrong_password: "Wrong Password" + upload_picture: "Uploadează o imagine" + delete_this_account: "Ștergere permanetă a acestui cont" +# god_mode: "God Mode" password_tab: "Parolă" emails_tab: "Email-uri" admin: "Admin" new_password: "Parolă nouă" new_password_verify: "Verifică" + type_in_email: "Scrie adresa de email ca să confirmi ștergerea" # {change} +# type_in_password: "Also, type in your password." email_subscriptions: "Subscripție Email" email_subscriptions_none: "Nu ai subscripții Email." email_announcements: "Anunțuri" @@ -463,7 +714,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman email_notifications: "Notificări" email_notifications_summary: "Control pentru notificări email personalizate, legate de activitatea CodeCombat." email_any_notes: "Orice Notificări" - email_any_notes_description: "Dezactivați pentru a opri toate e-mailurile de notificare a activității. Disable to stop all activity notification emails." + email_any_notes_description: "Dezactivați pentru a opri toate e-mailurile de notificare a activității." email_news: "Noutăți" email_recruit_notes: "Oportunități de job-uri" email_recruit_notes_description: "Daca joci foarte bine, este posibil sa te contactăm pentru obținerea unui loc (mai bun) de muncă." @@ -476,99 +727,162 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman saved: "Modificări salvate" password_mismatch: "Parola nu se potrivește." password_repeat: "Te rugăm sa repeți parola." -# job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated -# job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." -# job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." + job_profile: "Profil Job" # Rest of this section (the job profile stuff and wizard stuff) is deprecated + job_profile_approved: "Profilul de job a fost aprobat de CodeCombat Angajatorii pot să il vadă pană îl marchezi ca inactiv sau dacă nu e schimbat timp de 4 săptămâni." + job_profile_explanation: "Salut! Completează formularul, și te vom contacta ca să iți gasești un job ca software developer." sample_profile: "Vezi un profil exemplu" view_profile: "Vizualizează Profilul" - wizard_tab: "Wizard" - wizard_color: "Culoare haine pentru Wizard" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." + keyboard_shortcuts: + keyboard_shortcuts: "Scurtături Keyboard" + space: "Space" + enter: "Enter" +# press_enter: "press enter" + escape: "Escape" + shift: "Shift" + run_code: "Rulează codul." + run_real_time: "Rulează în timp real." + continue_script: "Continue past current script." + skip_scripts: "Treci peste toate script-urile ce pot fi sărite." + toggle_playback: "Comută play/pause." + scrub_playback: "Mergi înainte si înapoi in timp." + single_scrub_playback: "Mergi înainte si înapoi in timp cu un singur cadru." + scrub_execution: "Mergi prin lista curentă de vrăji executate." + toggle_debug: "Comută afișaj debug." + toggle_grid: "Comută afișaj grilă." + toggle_pathfinding: "Comută afișaj pathfinding." + beautify: "Înfrumusețează codul standardizând formatarea lui." + maximize_editor: "Mărește/Micește editorul." -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + community: + main_title: "Comunitatea CodeCombat" + introduction: "Vezi metode prin care poți să te implici și tu mai jos și decide să alegi ce ți se pare cel mai distractiv. Deabia așteptăm să lucrăm împreună!" + level_editor_prefix: "Folosește CodeCombat" + level_editor_suffix: "Pentru a crea și a edita nivele. Useri au creat nivele pentru clasele lor, prieteni, hackathonuri, studenți si rude. Dacă crearea unui nivel nou ți se pare intimidant poți sa modifici un nivel creat de noi!" + thang_editor_prefix: "Numim unitățile din joc 'thangs'. Folosește" + thang_editor_suffix: "pentru a modifica ilustrațile sursă CodeCombat. Permitele unitătilor sa arunce proiectile, schimbă direcția unei animații, schimbă viața unei unități, sau uploadează propiile sprite-uri vectoriale." + article_editor_prefix: "Vezi o greșală in documentația noastă? Vrei să documentezi instrucțiuni pentru propiile creații? Vezi" + article_editor_suffix: "si ajută jucători CodeCombat să obțină căt mai multe din playtime-ul lor." + find_us: "Ne găsești pe aceste site-uri" + social_blog: "Citește blogul CodeCombat pe Sett" + social_discource: "Alăturăte discuțiilor pe forumul Discourse" + social_facebook: "Lasă un Like pentru CodeCombat pe facebook" + social_twitter: "Urmărește CodeCombat pe Twitter" + social_gplus: "Alăturăte pe Google+" + social_hipchat: "Vorbește cu noi pe chatul public HipChat camera CodeCombat" + contribute_to_the_project: "Contribuie la proiect" + + clans: + clan: "Clan" + clans: "Clanuri" + new_name: "Nume nou de clan" + new_description: "Descrierea clanului nou" + make_private: "Fă clanul privat" + subs_only: "numai abonați" + create_clan: "Creează un clan Nou" +# private_preview: "Preview" + public_clans: "Clanuri Publice" + my_clans: "Clanurile mele" + clan_name: "Numele Clanului" + name: "Nume" + chieftain: "Chieftain" + type: "Tip" + edit_clan_name: "Editează numele clanului" + edit_clan_description: "Editează descrierea clanului" + edit_name: "editează nume" + edit_description: "editează descriere" + private: "(privat)" + summary: "Sumar" + average_level: "Medie Level" + average_achievements: "Medie Achievements" + delete_clan: "Șterge Clan" + leave_clan: "Pleacă din Clan" + join_clan: "Intră în Clan" + invite_1: "Invitație:" + invite_2: "*Invită jucători in acest clan trimițându-le acest link." + members: "Membrii" + progress: "Progres" + not_started_1: "neînceput" + started_1: "început" + complete_1: "complet" + exp_levels: "Extinde nivele" + rem_hero: "Șterge Eroul" + status: "Stare" + complete_2: "Complet" + started_2: "Început" + not_started_2: "Neînceput" + view_solution: "Click pentru a vedea soluția." + latest_achievement: "Ultimile Achievement-uri" + playtime: "Timp Jucat" + last_played: "Ultima oară cănd ai jucat" classes: archmage_title: "Archmage" archmage_title_description: "(Programator)" + archmage_summary: "Dacă ești un dezvoltator interesat să programezi jocuri educaționale, devino Archmage si ajută-ne să construim CodeCombat!" artisan_title: "Artizan" artisan_title_description: "(Creator de nivele)" + artisan_summary: "Construiește si oferă nivele pentru tine si pentru prieteni tăi, ca să se joace. Devino Artisan si învață arta de a împărți cunoștințe despre programare." adventurer_title: "Aventurier" adventurer_title_description: "(Playtester de nivele)" + adventurer_summary: "Primește nivelele noastre noi (chiar si cele pentru abonați) gratis cu o săptămână înainte si ajută-ne să reparăm bug-uri până la lansare." scribe_title: "Scrib" scribe_title_description: "(Editor de articole)" + scribe_summary: "Un cod bun are nevoie de o documentație bună. Scrie, editează, si improvizează documentația citită de milioane de jucători în întreaga lume." diplomat_title: "Diplomat" diplomat_title_description: "(Translator)" + diplomat_summary: "CodeCombat e localizat în 45+ de limbi de Diplomații noștri. Ajută-ne și contribuie la traducere." ambassador_title: "Ambasador" ambassador_title_description: "(Suport)" + ambassador_summary: "Îmblânzește useri de pe forumul nostru si oferă direcți pentru cei cu întrebări. Ambasadori noștri reprezintă CodeCombat în fața lumii." editor: main_title: "Editori CodeCombat" article_title: "Editor Articol" thang_title: "Editor Thang" level_title: "Editor Nivele" -# achievement_title: "Achievement Editor" -# back: "Back" + achievement_title: "Editor Achievement" + poll_title: "Editor Sondaje" + back: "Înapoi" revert: "Revino la versiunea anterioară" revert_models: "Resetează Modelele" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." -# generate_terrain: "Generate Terrain" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" + pick_a_terrain: "Alege Terenul" + dungeon: "Temniță" + indoor: "Interior" + desert: "Deșert" + grassy: "Ierbos" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Mic" + large: "Mare" + fork_title: "Fork Versiune Nouă" + fork_creating: "Creare Fork..." + generate_terrain: "Generează Teren" + more: "Mai Multe" + wiki: "Wiki" + live_chat: "Chat Live" + thang_main: "Principal" + thang_spritesheets: "Spritesheets" + thang_colors: "Culori" level_some_options: "Opțiuni?" level_tab_thangs: "Thangs" level_tab_scripts: "Script-uri" level_tab_settings: "Setări" level_tab_components: "Componente" level_tab_systems: "Sisteme" -# level_tab_docs: "Documentation" + level_tab_docs: "Documentație" level_tab_thangs_title: "Thangs actuali" -# level_tab_thangs_all: "All" + level_tab_thangs_all: "Toate" level_tab_thangs_conditions: "Condiți inițiale" level_tab_thangs_add: "Adaugă Thangs" -# delete: "Delete" -# duplicate: "Duplicate" -# rotate: "Rotate" +# level_tab_thangs_search: "Search thangs" + add_components: "Adaugă Componente" + component_configs: "Configurarea Componentelor" + config_thang: "Dublu click pentru a configura un thang" + delete: "Șterge" + duplicate: "Duplică" + stop_duplicate: "Oprește Duplicarea" + rotate: "Rotește" level_settings_title: "Setări" level_component_tab_title: "Componente actuale" level_component_btn_new: "Crează componentă nouă" @@ -577,7 +891,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman level_systems_btn_add: "Adaugă Sistem" level_components_title: "Înapoi la toți Thangs" level_components_type: "Tip" - level_component_edit_title: "Editează Componenta" + level_component_edit_title: "Editează Componentă" level_component_config_schema: "Schema Config" level_component_settings: "Setări" level_system_edit_title: "Editează Sistem" @@ -587,54 +901,54 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman new_article_title: "Crează un articol nou" new_thang_title: "Crează un nou tip de Thang" new_level_title: "Crează un nivel nou" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" + new_article_title_login: "Loghează-te pentru a crea un Articol Nou" + new_thang_title_login: "Loghează-te pentru a crea un Thang de Tip Nou" + new_level_title_login: "Loghează-te pentru a crea un Nivel Nou" + new_achievement_title: "Crează un Achivement Nou" + new_achievement_title_login: "Loghează-te pentru a crea un Achivement Nou" + new_poll_title: "Crează un Sondaj Nou" + new_poll_title_login: "Loghează-te pentru a crea un Sondaj Nou" article_search_title: "Caută articole aici" thang_search_title: "Caută tipuri de Thang aici" level_search_title: "Caută nivele aici" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" -# pop_i18n: "Populate I18N" + achievement_search_title: "Caută Achievements" + poll_search_title: "Caută Sondaje" + read_only_warning2: "Notă: nu poți salva editările aici, pentru că nu ești logat." + no_achievements: "Nici-un achivement adăugat acestui nivel până acum." + achievement_query_misc: "Key achievement din diverse" + achievement_query_goals: "Key achievement din obiectivele nivelelor" + level_completion: "Finalizare Nivel" + pop_i18n: "Populează I18N" + tasks: "Sarcini" + clear_storage: "Șterge schimbările locale" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Preview" edit_article_title: "Editează Articol" + polls: + priority: "Prioritate" + contribute: page_title: "Contribuțtii" - character_classes_title: "Clase de caractere" - introduction_desc_intro: "Avem speranțe mari pentru CodeCombat." - introduction_desc_pref: "Vrem să fie locul unde programatori de toate rangurile vin să învețe și să se distreze împreună, introduc pe alții in minunata lume a programării, și reflectă cele mai bune părți ale comunității. Nu vrem și nu putem să facem asta singuri; ceea ce face proiectele precum GitHub, Stack Overflow și Linux geniale sunt oameni care le folosesc și construiec peste ele. Cu scopul acesta, " - introduction_desc_github_url: "CodeCombat este complet open source" - introduction_desc_suf: ", și ne propunem să vă punem la dispoziție pe cât de mult posibil modalități de a lua parte la acest proiect pentru a-l face la fel de mult as vostru cât și al nostru." - introduction_desc_ending: "Sperăm să vă placă petrecerea noastră!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy și Matt" + intro_blurb: "CodeCombat este 100% open source! Sute de jucători dedicați ne-au ajutat sa construim jocul în cea ce este astăzi. Alătură-te si scrie următorul capitol în aventura CodeCombat de a ajuta lumea să învețe cod!" alert_account_message_intro: "Salutare!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "Interesat să lucrezi la grafica jocului, interfața grafică cu utilizatorul, baze de date și organizare server, multiplayer networking, fizică, sunet, sau performanțe game engine? Vrei să ajuți la construirea unui joc pentru a învăța pe alții ceea ce te pricepi? Avem o grămadă de făcut dacă ești un programator experimentat și vrei sa dezvolți pentru CodeCombat, această clasă este pentru tine. Ne-ar plăcea să ne ajuți să construim cel mai bun joc de programare făcut vreodată." - archmage_introduction: "Una dintre cele mai bune părți despre construirea unui joc este că sintetizează atât de multe lucruri diferite. Grafică, sunet, networking în timp real, social networking, și desigur multe dintre aspectele comune ale programării, de la gestiune low-level a bazelor de date, și administrare server până la construirea de interfețe. Este mult de muncă, și dacă ești un programator cu experiență, cu un dor de a se arunca cu capul înainte îm CodeCombat, această clasă ți se potrivește. Ne-ar plăcea să ne ajuți să construim cel mai bun joc de programare făcut vreodată." + alert_account_message: "Pentru a te abona la mailurile clasei trebuie să fi logat." + archmage_introduction: "Una dintre cele mai bune părți despre construirea unui joc este că sintetizează atât de multe lucruri diferite. Grafică, Sunet, Networking în timp real, Social Networking, și desigur multe dintre aspectele comune ale programării, de la gestiune low-level a bazelor de date, și administrare server până la construirea de interfețe. Este mult de muncă, și dacă ești un programator cu experiență, cu un dor de a se arunca cu capul înainte îm CodeCombat, această clasă ți se potrivește. Ne-ar plăcea să ne ajuți să construim cel mai bun joc de programare făcut vreodată." class_attributes: "Atribute pe clase" archmage_attribute_1_pref: "Cunoștințe în " archmage_attribute_1_suf: ", sau o dorință de a învăța. Majoritatea codului este în acest limbaj. Dacă ești fan Ruby sau Python, te vei simți ca acasă. Este JavaScript, dar cu o sintaxă mai frumoasă." archmage_attribute_2: "Ceva experiență în programare și inițiativă personală. Te vom ajuta să te orientezi, dar nu putem aloca prea mult timp pentru a te pregăti." how_to_join: "Cum să ni te alături" join_desc_1: "Oricine poate să ajute! Doar intrați pe " - join_desc_2: "pentru a începe, și bifați căsuța de dedesubt pentru a te marca ca un Archmage curajos și pentru a primi ultimele știri pe email. Vrei să discuți despre ce să faci sau cum să te implici mai mult? " + join_desc_2: "pentru a începe, bifați căsuța de dedesubt pentru a te marca ca un Archmage curajos și pentru a primi ultimele știri pe email. Vrei să discuți despre ce să faci sau cum să te implici mai mult? " join_desc_3: ", sau găsește-ne în " join_desc_4: "și pornim de acolo!" join_url_email: "Trimite-ne Email" join_url_hipchat: "public HipChat room" - more_about_archmage: "Învață mai multe despre cum să devi un Archmage" archmage_subscribe_desc: "Primește email-uri despre noi oportunități de progrmare și anunțuri." - artisan_summary_pref: "Vrei să creezi nivele și să extinzi arsenalul CodeCombat? Oamenii ne termină nivelele mai repede decât putem să le creăm! Momentan, editorul nostru de nivele este rudimentar, așa că aveți grijă. Crearea de nivele va fi o mică provocare și va mai avea câteva bug-uri. Dacă ai viziuni cu campanii care cuprind loop-uri for pentru" - artisan_summary_suf: ", atunci asta e clasa pentru tine." artisan_introduction_pref: "Trebuie să construim nivele adiționale! Oamenii sunt nerăbdători pentru mai mult conținut, și noi putem face doar atât singuri. Momentan editorul de nivele abia este utilizabil până și de creatorii lui, așa că aveți grijă. Dacă ai viziuni cu campanii care cuprind loop-uri for pentru" artisan_introduction_suf: ", atunci aceasta ar fi clasa pentru tine." artisan_attribute_1: "Orice experiență în crearea de conținut ca acesta ar fi de preferat, precum folosirea editoarelor de nivele de la Blizzard. Dar nu este obligatoriu!" @@ -645,48 +959,38 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman artisan_join_step2: "Crează un nivel nou și explorează nivelele deja existente." artisan_join_step3: "Găsește-ne pe chatul nostru de Hipchat pentru ajutor." artisan_join_step4: "Postează nivelele tale pe forum pentru feedback." - more_about_artisan: "Învață mai multe despre ce înseamnă să devi un Artizan" artisan_subscribe_desc: "Primește email-uri despre update-uri legate de Editorul de Nivele și anunțuri." - adventurer_summary: "Să fie clar ce implică rolul tău: tu ești tancul. Vei avea multe de îndurat. Avem nevoie de oameni care să testeze nivelele noi și să ne ajute să găsim moduri noi de a le îmbunătăți. Va fi greu; să creezi jocuri bune este un proces dificil și nimeni nu o face perfect din prima. Dacă crezi că poți îndura , atunci aceasta este clasa pentru tine." - adventurer_introduction: "Să fie clar ce implică rolul tău: tu ești tancul. Vei avea multe de îndurat. Avem nevoie de oameni care să testeze nivelele noi și să ne ajute să găsim moduri noi de a le îmbunătăți. Va fi greu; să creezi jocuri bune este un proces dificil și nimeni nu o face perfect din prima. Dacă crezi că poți îndura , atunci aceasta este clasa pentru tine." + adventurer_introduction: "Să fie clar ce implică rolul tău: tu ești tancul. Vei avea multe de îndurat. Avem nevoie de oameni care să testeze nivelele noi și să ne ajute să găsim moduri noi de a le îmbunătăți. Va fi greu; să creezi jocuri bune este un proces dificil și nimeni nu o face perfect din prima. Dacă crezi că poți îndura, atunci aceasta este clasa pentru tine." adventurer_attribute_1: "O sete de cunoaștere. Tu vrei să înveți cum să programezi și noi vrem să te învățăm. Cel mai probabil tu vei fi cel care va preda mai mult în acest caz." adventurer_attribute_2: "Carismatic. Formulează într-un mod clar ceea ce trebuie îmbunătățit și oferă sugestii." - adventurer_join_pref: "Ori fă echipă (sau recrutează!) cu un Artizan și lucrează cu el, sau bifează căsuța de mai jos pentru a primi email când sunt noi nivele de testat. De asemenea vom posta despre nivele care trebuiesc revizuite pe rețelele noastre precum" + adventurer_join_pref: "Ori fă echipă (sau recrutează!) cu un Artizan și lucrează cu el, sau bifează căsuța de mai jos pentru a primi email când sunt noi nivele de testat. De asemenea vom posta despre nivele care trebuie revizuite pe rețelele noastre precum" adventurer_forum_url: "forumul nostru" - adventurer_join_suf: "deci dacă preferi să fi înștiințat în acele moduri ,înscrie-te acolo!" - more_about_adventurer: "Învață mai multe despre ce înseamnă să devi un Aventurier" + adventurer_join_suf: "deci dacă preferi să fi înștiințat în acele moduri, înscrie-te acolo!" adventurer_subscribe_desc: "Primește email-uri când sunt noi nivele de testat." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " -# scribe_introduction_url_mozilla: "Mozilla Developer Network" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." -# contact_us_url: "Contact us" -# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" -# scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." -# diplomat_introduction_pref: "So, if there's one thing we learned from the " -# diplomat_launch_url: "launch in October" -# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." -# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." -# diplomat_join_pref_github: "Find your language locale file " -# diplomat_github_url: "on GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" -# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." -# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" -# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" -# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." + scribe_introduction_pref: "CodeCombat nu o să fie doar o colecție de nivele. Vor fi incluse resurse de cunoaștere, un wiki despre concepte de programare legate de fiecare nivel. În felul acesta fiecare Arisan nu trebuie să mai descrie în detaliu ce este un operator de comparație, ei pot să pună un link la un Articol mai bine documentat. Ceva asemănător cu ce " + scribe_introduction_url_mozilla: "Mozilla Developer Network" + scribe_introduction_suf: " a construit. Dacă idea ta de distracție este să articulezi conceptele de programare în formă Markdown, această clasă ți s-ar potrivi." + scribe_attribute_1: "Un talent în cuvinte este tot ce îți trebuie. Nu numai gramatică și ortografie, trebuie să poți să explici ideii complicate celorlați." + contact_us_url: "Contactați-ne" + scribe_join_description: "spune-ne câte ceva despre tine, experiențele tale despre programare și ce fel de lucruri ți-ar place să scri despre. Vom începe de acolo!." + scribe_subscribe_desc: "Primește mailuri despre scrisul de articole." + diplomat_introduction_pref: "Dacă ar fi un lucru care l-am învățat din " + diplomat_launch_url: "lansarea din Octombire" + diplomat_introduction_suf: "acesta ar fi că: există un interes mare pentru CodeCombat și în alte țări! Încercăm sa adunăm cât mai mulți translatori care sunt pregătiți să transforme un set de cuvinte intr-un alt set de cuvinte ca să facă CodeCombat cât mai accesibil în toată lumea. Dacă vrei să tragi cu ochiul la conțintul ce va apărea și să aduci nivele cât mai repede pentru conaționali tăi, această clasă ți se potriveste." + diplomat_attribute_1: "Fluență în Engleză și limba în care vrei să traduci. Când explici ideii complicate este important să întelegi bine ambele limbi!" + diplomat_i18n_page_prefix: "Poți începe să traduci nivele accesând" + diplomat_i18n_page: "Pagina de traduceri" + diplomat_i18n_page_suffix: ", sau interfața si website-ul pe GitHub." + diplomat_join_pref_github: "Găsește fișierul pentru limba ta " + diplomat_github_url: "pe GitHub" + diplomat_join_suf_github: ", editeazăl online si trimite un pull request. Bifează căsuța de mai jos ca să fi up-to-date cu dezvoltările noastre internaționale!" + diplomat_subscribe_desc: "Primește mail-uri despre dezvoltările i18n si niveluri de tradus." + ambassador_introduction: "Aceasta este o comunitate pe care o construim, iar voi sunteți conexiunile. Avem forumui, email-uri, si rețele sociale cu mulți oameni cu care se poate vorbi despre joc și de la care se poate învața. Dacă vrei să ajuți oameni să se implice și să se distreze această clasă este potrivită pentru tine." + ambassador_attribute_1: "Abilități de comunicare. Abilitatea de a indentifica problemele pe care jucătorii le au si șa îi poti ajuta. De asemenea, trebuie să ne informezi cu părerile jucătoriilor, ce le place și ce vor mai mult!" + ambassador_join_desc: "spune-ne câte ceva despre tine, ce ai făcut si ce te interesează să faci. Vom porni de acolo!." + ambassador_join_note_strong: "Notă" + ambassador_join_note_desc: "Una din prioritățile noaste este să constrruim un joc multiplayer unde jucători noștri, dacă au probleme pot să cheme un wizard cu un nivel ridicat să îi ajute." + ambassador_subscribe_desc: "Primește mailuri despre support updates și dezvoltări multiplayer." changes_auto_save: "Modificările sunt salvate automat când apeși checkbox-uri." diligent_scribes: "Scribii noștri:" powerful_archmages: "Bravii noștri Archmage:" @@ -706,7 +1010,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman games_simulated_for: "Jocuri simulate pentru tine:" games_simulated: "Jocuri simulate" games_played: "Jocuri jucate" - ratio: "Ratie" + ratio: "Rație" leaderboard: "Clasament" battle_as: "Luptă ca " summary_your: "Al tău " @@ -719,153 +1023,213 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman rank_submitted: "Se trimite pentru Clasament" rank_failed: "A eșuat plasarea in clasament" rank_being_ranked: "Jocul se plasează in Clasament" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" + rank_last_submitted: "trimis " + help_simulate: "Ne ajuți simulând jocuri?" code_being_simulated: "Codul tău este simulat de alți jucători pentru clasament. Se va actualiza cum apar meciuri." no_ranked_matches_pre: "Nici un meci de clasament pentru " - no_ranked_matches_post: " echipă! Joacă împotriva unor concurenți și revino apoi aici pentr a-ți plasa meciul in clasament." + no_ranked_matches_post: " echipă! Joacă împotriva unor concurenți și revino apoi aici pentru a-ți plasa meciul in clasament." choose_opponent: "Alege un adversar" select_your_language: "Alege limbă!" tutorial_play: "Joacă Tutorial-ul" tutorial_recommended: "Recomandat dacă nu ai mai jucat niciodată înainte" - tutorial_skip: "Sari peste Tutorial" + tutorial_skip: "Sări peste Tutorial" tutorial_not_sure: "Nu ești sigur ce se întâmplă?" tutorial_play_first: "Joacă Tutorial-ul mai întâi." simple_ai: "AI simplu" warmup: "Încălzire" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + friends_playing: "Prieteni ce se Joacă" + log_in_for_friends: "Loghează-te ca să joci cu prieteni tăi!" + social_connect_blurb: "Conectează-te și joacă împotriva prietenilor tăi!" + invite_friends_to_battle: "Invită-ți prieteni să se alăture bătăliei" + fight: "Luptă!" + watch_victory: "Vizualizează victoria" + defeat_the: "Învinge" + tournament_started: ", a început" + tournament_ends: "Turneul se termină" + tournament_ended: "Turneul s-a terminat" + tournament_rules: "Regulile Turneului" + tournament_blurb: "Scrie cod, colectează aur, construiește armate, distruge inamici, câștigă premii, si îmbunătățeșteți cariera în turneul Lăcomiei de $40,000! Află detalii" + tournament_blurb_criss_cross: "Caștigă pariuri, creează căi, păcălește-ți oponenți, strâange Pietre Prețioase, si îmbunătățeșteți cariera in turneul Criss-Cross! Află detalii" + tournament_blurb_zero_sum: "Dezlănțuie creativitatea de programare în strângerea de aur sau în tactici de bătălie în alpine mirror match dintre vrăitori roșii și cei albaștrii.Turneul începe Vineri, 27 Martie și se va desfăsura până Luni, 6 Aprilie la 5PM PDT. Află detalii" + tournament_blurb_blog: "pe blogul nostru" + rules: "Reguli" + winners: "Învingători" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + user: + stats: "Statistici" + singleplayer_title: "Nivele Singleplayer" + multiplayer_title: "Nivele Multiplayer" + achievements_title: "Achievement-uri" + last_played: "Ultima oară jucat" + status: "Stare" + status_completed: "Complet" + status_unfinished: "Neterminat" + no_singleplayer: "Nici-un joc Singleplayer jucat." + no_multiplayer: "Nici-un joc Multiplayer jucat." + no_achievements: "Nici-un Achivement câștigat." + favorite_prefix: "Limbaj preferat" + favorite_postfix: "." + not_member_of_clans: "Nu ești membrul unui clan." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + achievements: + last_earned: "Ultimul câstigat" + amount_achieved: "Sumă" + achievement: "Achievement" + category_contributor: "Contribuitor" + category_ladder: "Ladder" + category_level: "Nivel" + category_miscellaneous: "Diverse" + category_levels: "Nivele" + category_undefined: "Necategorizate" + current_xp_prefix: "" + current_xp_postfix: " în total" + new_xp_prefix: "" + new_xp_postfix: " câștigat" + left_xp_prefix: "" + left_xp_infix: " până la level" + left_xp_postfix: "" -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." + account: + recently_played: "Recent Jucat" + no_recent_games: "Nici-un joc jucat de 2 săptămâni." + payments: "Plăți" + purchased: "Cumpărate" + subscription: "Abonament" + invoices: "Invoice-uri" + service_apple: "Apple" + service_web: "Web" + paid_on: "Plătit pe" + service: "Service" + price: "Preț" + gems: "Pietre Prețioase" + active: "Activ" + subscribed: "Abonat" + unsubscribed: "Dezabonat" + active_until: "Activ până" + cost: "Cost" + next_payment: "Următoarea Plată" + card: "Card" + status_unsubscribed_active: "Nu ești abonat si nu vei fi facturat, contul tău este activ deocamdată." + status_unsubscribed: "Primește access la nivele noi, eroi, iteme, și Pietre Prețioase bonus cu un abonament CodeCombat!" -# loading_error: -# could_not_load: "Error loading from server" -# connection_failure: "Connection failed." -# unauthorized: "You need to be signed in. Do you have cookies disabled?" -# forbidden: "You do not have the permissions." -# not_found: "Not found." -# not_allowed: "Method not allowed." -# timeout: "Server timeout." -# conflict: "Resource conflict." -# bad_input: "Bad input." -# server_error: "Server error." -# unknown: "Unknown error." + account_invoices: + amount: "Sumă in dolari US" + declined: "Cardul tău a fost refuzat" + invalid_amount: "Introdu o sumă in dolari US." + not_logged_in: "Logheazăte sau crează un cont pentru a accesa invoice-uri." + pay: "Plată Invoice" + purchasing: "Cumpăr..." + retrying: "Eroare server, reîncerc." + success: "Plătit cu success. Mulțumim!" -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" -# social_network_apis: "Social Network APIs" -# facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" -# facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" -# gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" -# user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" -# patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" -# thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" -# feedback: "Feedback" + loading_error: + could_not_load: "Eroare la încărcarea pe server" + connection_failure: "Conexiune eșuată." + unauthorized: "Este nevoie să te loghezi. Ai cookies dezactivate?" + forbidden: "Nu ai permisiune." + not_found: "Nu a fost găsit." + not_allowed: "Metodă nepermisă." + timeout: "Timeout Server." + conflict: "Conflict resurse." + bad_input: "Date greșite." + server_error: "Eroare Server." + unknown: "Eroare Necunoscută." -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" + resources: + sessions: "Sesiuni" + your_sessions: "Sesiunile Tale" + level: "Nivel" + social_network_apis: "Social Network APIs" + facebook_status: "Facebook Status" + facebook_friends: "Prieteni Facebook" + facebook_friend_sessions: "Sesiunile prietenilor de pe Facebook" + gplus_friends: "Prieteni G+" + gplus_friend_sessions: "Sesiunile prietenilor de pe G+" + leaderboard: "Leaderboard" + user_schema: "Schema User" + user_profile: "Profil User" + patch: "Patch" + patches: "Patch-uri" + patched_model: "Document Sursă" + model: "Model" + system: "Sistem" + systems: "Sisteme" + component: "Componentă" + components: "Componente" + thang: "Thang" + thangs: "Thangs" + level_session: "Sesiunea Ta" + opponent_session: "Sesiunea Oponentului" + article: "Articol" + user_names: "Nume Useri" + thang_names: "Nume Thang-uri" + files: "Fișiere" + top_simulators: "Top Simulatori" + source_document: "Document Sursă" + document: "Document" + sprite_sheet: "Sprite Sheet" + employers: "Angajatori" + candidates: "Candidați" + candidate_sessions: "Sesiuni Candidați" + user_remark: "Remarcări User" + user_remarks: "Remarcări Useri" + versions: "Versiuni" + items: "Iteme" + hero: "Erou" + heroes: "Eroii" + achievement: "Achievement" + clas: "CLAs" + play_counts: "Jucate" + feedback: "Feedback" + payment_info: "Info Plăți" + campaigns: "Campanii" + poll: "Sondaj" + user_polls_record: "Istoric votări Sondaj" -# guide: -# temp: "Temp" +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" + + delta: + added: "Adăugat" + modified: "Modificat" +# not_modified: "Not Modified" + deleted: "Șters" + moved_index: "Index Mutat" + text_diff: "Diff Text" + merge_conflict_with: "ÎBINĂ CONFLICTUL CU" + no_changes: "Fară Schimbări" + + guide: + temp: "Temp" multiplayer: multiplayer_title: "Setări Multiplayer" # We'll be changing this around significantly soon. Until then, it's not important to translate. -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." + multiplayer_toggle: "Permite Multiplayer" + multiplayer_toggle_description: "Permitele altora să intre în jocul tău." multiplayer_link_description: "Împărtășește acest link cu cei care vor să ți se alăture." multiplayer_hint_label: "Hint:" multiplayer_hint: " Apasă pe link pentru a selecta tot, apoi apasă ⌘-C sau Ctrl-C pentru a copia link-ul." multiplayer_coming_soon: "Mai multe feature-uri multiplayer în curând!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." + multiplayer_sign_in_leaderboard: "Loghează-te sau crează un cont pentru a pune soluția pe leaderboard." legal: page_title: "Aspecte Legale" @@ -874,11 +1238,11 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman github_url: "pagina noastră de GitHub" opensource_description_center: "și ajută-ne dacă îți place! CodeCombat este construit peste o mulțime de proiecte open source, care noi le iubim. Vizitați" archmage_wiki_url: "Archmage wiki" - opensource_description_suffix: "pentru o listă cu software-ul care face acest joc posibil." + opensource_description_suffix: "pentru o listă cu software-ul care fac acest joc posibil." practices_title: "Convenții" practices_description: "Acestea sunt promisiunile noastre către tine, jucătorul, fără așa mulți termeni legali." privacy_title: "Confidenţialitate şi termeni" - privacy_description: "Noi nu vom vinde nici o informație personală. Intenționăm să obținem profit prin recrutare eventual, dar stați liniștiți , nu vă vom vinde informațiile personale companiilor interesate fără consimțământul vostru explicit." + privacy_description: "Nu o să iți vindem datele personale." security_title: "Securitate" security_description: "Ne străduim să vă protejăm informațiile personale. Fiind un proiect open-source, site-ul nostru oferă oricui posibilitatea de a ne revizui și îmbunătăți sistemul de securitate." email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman email_settings_url: "setările tale de email" email_description_suffix: " sau prin link-urile din email-urile care vi le trimitem, puteți să schimbați preferințele și să vâ dezabonați oricând." cost_title: "Cost" - cost_description: "Momentan, CodeCombat este 100% gratis! Unul dintre obiectele noastre principale este să îl menținem așa, astfel încât să poată juca cât mai mulți oameni. Dacă va fi nevoie , s-ar putea să percepem o plată pentru o pentru anumite servici,dar am prefera să nu o facem. Cu puțin noroc, vom putea susține compania cu:" - recruitment_title: "Recrutare" - recruitment_description_prefix: "Aici la CodeCombat, vei deveni un vrăjitor puternic nu doar în joc, ci și în viața reală." - url_hire_programmers: "Nimeni nu poate angaja programatori destul de rapid" - recruitment_description_suffix: "așa că odată ce ți-ai dezvoltat abilitățile și esti de acord, noi vom trimite un demo cu cele mai bune realizări ale tale către miile de angajatori care se omoară să pună mâna pe tine. Pe noi ne plătesc puțin, pe tine te vor plăti" - recruitment_description_italic: "mult" - recruitment_description_ending: "site-ul rămâne gratis și toată lumea este fericită. Acesta este planul." + cost_description: "Momentan, CodeCombat este 100% gratis! Unul dintre obiectele noastre principale este să îl menținem așa, astfel încât să poată juca cât mai mulți oameni. Dacă va fi nevoie , s-ar putea să percepem o plată pentru anumite servici, dar am prefera să nu o facem. Cu puțin noroc, vom putea susține compania cu:" copyrights_title: "Drepturi de autor și licențe" contributor_title: "Acord de licență Contributor" contributor_description_prefix: "Toți contribuitorii, atât pe site cât și pe GitHub-ul nostru, sunt supuși la" @@ -912,9 +1270,9 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman art_sprites: "Sprites" art_other: "Orice si toate celelalte creații non-cod care sunt disponibile când se crează nivele." art_access: "Momentan nu există nici un sistem universal,ușor pentru preluarea acestor bunuri. În general, preluați-le precum site-ul din URL-urile folosite, contactați-ne pentru asistență, sau ajutați-ne sa extindem site-ul pentru a face aceste bunuri mai ușor accesibile." - art_paragraph_1: "Pentru atribuire, vă rugăm numiți și lăsați referire link la codecombat.com unde este folosită sursa sau unde este adecvat pentru mediu. De exemplu:" + art_paragraph_1: "Pentru atribuire, vă rugăm numiți și lăsați referire link la codecombat.com unde este folosită sursa sau unde este adecvat pentru mediu. De exemplu:" use_list_1: "Dacă este folosit într-un film sau alt joc, includeți codecombat.com la credite." - use_list_2: "Dacă este folosit pe un site, includeți un link in apropiere, de exemplu sub o imagine, sau in pagina generală de atribuiri unde menționați și alte Bunuri Creative și software open source folosit pe site. Ceva care face referință explicit la CodeCombat, precum o postare pe un blog care menționează CodeCombat, nu trebuie să facă o atribuire separată." + use_list_2: "Dacă este folosit pe un site, includeți un link in apropiere, de exemplu sub o imagine, sau in pagina generală de atribuiri unde menționați și alte Bunuri Creative și software open source folosit pe site. Ceva care face referință explicit la CodeCombat, precum o postare pe un blog care menționează CodeCombat, nu trebuie să se facă o atribuire separată." art_paragraph_2: "Dacă conținutul folosit nu este creat de către CodeCombat ci de către un utilizator al codecombat.com,atunci faceți referință către ei, și urmăriți indicațiile de atribuire prevăzute în descrierea resursei dacă există." rights_title: "Drepturi rezervate" rights_desc: "Toate drepturile sunt rezervate pentru Nivele în sine. Asta include" @@ -928,209 +1286,193 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman nutshell_description: "Orice resurse vă punem la dispoziție în Editorul de Nivele puteți folosi liber cum vreți pentru a crea nivele. Dar ne rezervăm dreptul de a rezerva distribuția de nivele în sine (care sunt create pe codecombat.com) astfel încât să se poată percepe o taxă pentru ele pe vitor, dacă se va ajunge la așa ceva." canonical: "Versiunea in engleză a acestui document este cea definitivă, versiunea canonică. Dacă există orice discrepanțe între traduceri, documentul in engleză are prioritate." -# ladder_prizes: -# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - - wizard_settings: - title: "Setări Wizard" - customize_avatar: "Personalizează-ți Avatarul" - active: "Activ" - color: "Culoare" - group: "Grup" - clothes: "Haine" - trim: "Margine" - cloud: "Nor" - team: "Echipa" - spell: "Vrajă" - boots: "Încălțăminte" - hue: "Nuanță" - saturation: "Saturație" - lightness: "Luminozitate" + ladder_prizes: + title: "Premii Turnee" # This section was for an old tournament and doesn't need new translations now. + blurb_1: "Aceste premii se acordă în funcție de" + blurb_2: "Regulile Turneului" + blurb_3: "la jucători umani sau ogre de top." + blurb_4: "Două echipe înseamnă dublul premiilor!" + blurb_5: "(O să fie 2 câștigători pe primul loc, 2 pe locul 2, etc.)" + rank: "Rank" + prizes: "Premii" + total_value: "Valoare Totala" + in_cash: "în cash" + custom_wizard: "Wizard CodeCombat personalizat" + custom_avatar: "Avatar CodeCombat personalizat" + heap: "pentru 6 luni de acces \"Startup\"" + credits: "credite" + one_month_coupon: "coupon: alege Rails sau HTML" + one_month_discount: "discount, 30% off: choose either Rails or HTML" + license: "licență" + oreilly: "ebook la alegere" account_profile: settings: "Setări" # We are not actively recruiting right now, so there's no need to add new translations for this section. - edit_profile: "Modifica Profil" + edit_profile: "Modifică Profil" done_editing: "Am terminat modificările." profile_for_prefix: "Profil pentru " profile_for_suffix: "" featured: "Recomandate" -# not_featured: "Not Featured" -# looking_for: "Looking for:" -# last_updated: "Last updated:" -# contact: "Contact" + not_featured: "Nerecomandate" + looking_for: "Caută:" + last_updated: "Listă updatată:" + contact: "Contact" active: "Caut oferte de interviu." inactive: "Nu caut oferte" complete: "complet" - next: "Urmatorul" - next_city: "oras?" + next: "Următorul" + next_city: "oraș?" next_country: "alege țara." next_name: "nume?" - next_short_description: "scrie o scurta descriere." + next_short_description: "scrie o scurtă descriere." next_long_description: "descrie poziția dorită." - next_skills: "listeaza cel puțin cinci competențe." + next_skills: "listează cel puțin cinci competențe." next_work: "cronica istoricului dvs. de lucru." next_education: "povesteste-ne de chinurile educaționale" next_projects: "scoate in evidență pana la 3 proiecte la care ai lucrat." - next_links: "adăuga orice link-uri personale sau sociale." - next_photo: "adăuga o fotografie profesionala opțională." - next_active: "indica că esti deschis la oferte ca să apară în căutări." + next_links: "adăugă orice link-uri personale sau sociale." + next_photo: "adăugă o fotografie profesionala opțională." + next_active: "indică că esti deschis la oferte ca să apară în căutări." example_blog: "Blog" example_personal_site: "Site Personal" links_header: "Link-uri Personale" links_blurb: "Link către orice alte site-uri sau profiluri pe care doriți să se sublinieze, ca GitHub, LinkedIn, sau blog-ul personal." links_name: "Nume Link" - links_name_help: "Catre ce faci link?" + links_name_help: "Către ce faci link?" links_link_blurb: "Link URL" basics_header: "Actualizați informații de bază" basics_active: "Deschis la Oferte" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" -# basics_city: "City" -# basics_city_help: "City you want to work in (or live in now)." -# basics_country: "Country" -# basics_country_help: "Country you want to work in (or live in now)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" -# basics_looking_for_full_time: "Full-time" -# basics_looking_for_part_time: "Part-time" -# basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." -# work_experience: "Work Experience" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" -# work_duration_help: "When did you hold this gig?" -# work_description: "Description" -# work_description_help: "What did you do there? (140 chars; optional)" -# education: "Education" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." -# education_school: "School" -# education_school_help: "Name of your school." -# education_degree: "Degree" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" -# our_notes: "CodeCombat's Notes" -# remarks: "Remarks" -# projects: "Projects" -# projects_header: "Add 3 projects" -# projects_header_2: "Projects (Top 3)" -# projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" -# project_name_help: "What was the project called?" -# project_description: "Description" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" -# project_link_help: "Link to the project." -# player_code: "Player Code" + basics_active_help: "Vrei oferte de interviu chiar acum?" + basics_job_title: "Titlul jobului dorit" + basics_job_title_help: "What role are you looking for?" + basics_city: "Oraș" + basics_city_help: "Orașul în care vrei să muncești (sau unde locuiești acum)." + basics_country: "Țara" + basics_country_help: "Țara în care vrei să muncești (sau locuiești acum)." + basics_visa: "Stare Muncă in US" + basics_visa_help: "Ești autorizat să muncești în US sau ai nevoie de sponsorizare de viză? (Dacă locuești în Canada sau Australia, bifează autorizat.)" + basics_looking_for: "Caut" + basics_looking_for_full_time: "Full-time" + basics_looking_for_part_time: "Part-time" + basics_looking_for_remote: "La distanță" + basics_looking_for_contracting: "Contractant" + basics_looking_for_internship: "Internship" + basics_looking_for_help: "Ce fel de poziție ca Dezvoltator vrei?" + name_header: "Scrieți numele" + name_anonymous: "Dezvoltator Anonim" + name_help: "Numele pe care vrei ca angajatori să îl vadă, ex: 'Nick Winter'." + short_description_header: "Scrie o descriere scurtă despre tine" + short_description_blurb: "Adaugă un slogan ca un angajator să te cunoască mai repede." + short_description: "Slogan" + short_description_help: "Cine ești, și ce te interesează? 140 charactere max." + skills_header: "Aptitudini" + skills_help: "Alege aptitudini de dezvoltator relevante în ordinea experienței." + long_description_header: "Descrie poziția preferată" + long_description_blurb: "Spune-le angajatorilor cat de minunat ești si ce rol vrei." + long_description: "Descrierea Ta" + long_description_help: "Descrie-te la potențiali angajatori. Să fie scurt si la obiect. Recomandăm să subliniezi poziția care te interesează. Un Markdown savuros este ok; 600 charactere max." + work_experience: "Experiența Muncă" + work_header: "Istoricul locurilor undei ai muncit" + work_years: "Ani de experiență" + work_years_help: "Câți ani de experiență profesională (în care ai fost plătit) ca dezvoltator software ai?" + work_blurb: "Listează experiența de lucru relevantă, cea mai recentă prima." + work_employer: "Angajator" + work_employer_help: "Numele angajatorului." + work_role: "Titlul Jobului" + work_role_help: "Care a fost titlul jobului tău sau rolul?" + work_duration: "Durata" + work_duration_help: "Cand ai avut acest job?" + work_description: "Descriere" + work_description_help: "Ce ai făcut acolo? (14 char: opțional) " + education: "Educație" + education_header: "Povestește-ne chinurile academice" + education_blurb: "Listează informații despre educația ta." + education_school: "Școală" + education_school_help: "Numele Școlii" + education_degree: "Grad" + education_degree_help: "Care a fost gradul si domeniul de studiu?" + education_duration: "Date" + education_duration_help: "Când?" + education_description: "Descriere" + education_description_help: "Evidențiază orice despre experiența ta educațională. (140 char; opțional)" + our_notes: "Note CodeCombat" + remarks: "Observații" + projects: "Proiecte" + projects_header: "Adaugă 3 proiecte" + projects_header_2: "Proiecte (Top 3)" + projects_blurb: "Evidențiază-ți proiectele să impresionezi angajatorii." + project_name: "Numele Proiectului" + project_name_help: "Cum s-a numit proiectul?" + project_description: "Descriere" + project_description_help: "Descrie pe scurt proiectul." + project_picture: "Poză" + project_picture_help: "Uploadează o poză de 230x115px sau mai mare cu proiectul." + project_link: "Link" + project_link_help: "Link la proiect." + player_code: "Codul Jucătorului" -# employers: -# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." -# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." -# hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. -# get_started: "Get Started" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." -# candidate_name: "Name" -# candidate_location: "Location" -# candidate_looking_for: "Looking For" -# candidate_role: "Role" -# candidate_top_skills: "Top Skills" -# candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" + employers: + deprecation_warning_title: "Ne pare rău, CodeCombat nu recrutează acum." + deprecation_warning: "Ne axăm pe nivelele pentru începători în loc să găsim dezvoltatori experți deocamdată." + hire_developers_not_credentials: "Angajează dezvoltatori, nu scrisori de acreditare." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. + get_started: "Începe" + already_screened: "Deja am verificat tehnic toți candidați noștri" + filter_further: ", dar poți să filtrezi mai departe:" + filter_visa: "Viză" + filter_visa_yes: "US Autorizat" + filter_visa_no: "Neauthorized" + filter_education_top: "Top Școală" + filter_education_other: "Altele" + filter_role_web_developer: "Web Developer" + filter_role_software_developer: "Software Developer" + filter_role_mobile_developer: "Mobile Developer" + filter_experience: "Experiență" + filter_experience_senior: "Senior" + filter_experience_junior: "Junior" + filter_experience_recent_grad: "Grad Recent" + filter_experience_student: "Student Facultate" + filter_results: "rezultate" + start_hiring: "Începe angajările." + reasons: "Trei motive pentru care să angajezi prin intermediul nostru:" + everyone_looking: "Toți de aici caută următoarea oportunitate." + everyone_looking_blurb: "Uită de 20% răspunsuri pe LinkedIn InMail. Toți cei pe care îi listăm pe acest site vor să își găsească următoarea poziție și vor răspunde la cerere cu o introducere. " + weeding: "Relaxsează-te; noi ne ocupăm de selecții." + weeding_blurb: "Fiecare jucător pe care îl listăm aici i-au fost testate abilitățile tehnice. De asemenea îi selecționăm telefonic și facem notițe despre profilul fiecăruia ca să iți economisim timpul." + pass_screen: "O să treacă examenul dvs. tehnic." + pass_screen_blurb: "Examinați codul fiecărui candidat înainte să îi contactați. Un angajator a constatat că de 5 ori mai mulți dezvoltatori au trecut examenul tehnic decât cei angajați de pe Hacker News." + make_hiring_easier: "Fă angajarea mai ușoară, te rugăm." + what: "Ce este CodeCombat?" + what_blurb: "CodeCombat este un joc multiplayer în browser ce te învața programare. Jucătorii scriu cod ca să iși controleze forțele împotriva celorlalți Dezvoltatori." + cost: "Căt de mult taxăm?" + cost_blurb: "Percepem 15% din salariul pe primul an si oferim o garanție 100% bani inapoi pentru 90 de zile. Nu taxăm candidați care sunt în cursul unui interviu la o companie." + candidate_name: "Nume" + candidate_location: "Locație" + candidate_looking_for: "Caută" + candidate_role: "Rol" + candidate_top_skills: "Top Skills" + candidate_years_experience: "Exp Ani" + candidate_last_updated: "Ultimul Update" + candidate_who: "Cine" + featured_developers: "Dezvoltatori Featured" + other_developers: "Alți Dezvoltatori" + inactive_developers: "Dezvoltatori Inactivi" admin: -# av_espionage: "Espionage" # Really not important to translate /admin controls. -# av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" -# av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" - av_title: "Admin vede" + av_espionage: "Spionaj" # Really not important to translate /admin controls. + av_espionage_placeholder: "Email sau username" + av_usersearch: "Căutare user" + av_usersearch_placeholder: "Email, username, nume, orice" + av_usersearch_search: "Caută" + av_title: "Afișaj Admin" av_entities_sub_title: "Entități" av_entities_users_url: "Utilizatori" av_entities_active_instances_url: "Instanțe active" -# av_entities_employer_list_url: "Employer List" -# av_entities_candidates_list_url: "Candidate List" -# av_entities_user_code_problems_list_url: "User Code Problems List" + av_entities_employer_list_url: "Lista Angajatoriilor" + av_entities_candidates_list_url: "Lista Candidațiilor" + av_entities_user_code_problems_list_url: "Listă probleme de cod de utilizator" av_other_sub_title: "Altele" av_other_debug_base_url: "Base (pentru debugging base.jade)" u_title: "Listă utilizatori" -# ucp_title: "User Code Problems" + ucp_title: "Probleme de cod de utilizator" lg_title: "Ultimele jocuri" clas: "CLAs" diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index ac89842c0..e9c66cabf 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -3,14 +3,15 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi slogan: "Научитесь программировать, играя в игру" no_ie: "CodeCombat не работает в IE8 или более старых версиях. Нам очень жаль!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat не приспособлен для работы на мобильных устройствах и может не работать!" # Warning that shows up on mobile devices - play: "Играть" # The big play button that just starts playing a level + play: "Играть" # The big play button that opens up the campaign view. old_browser: "Ой, ваш браузер слишком стар для запуска CodeCombat. Извините!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Вы всё равно можете попробовать, но, скорее всего, это не будет работать." + ipad_browser: "Плохие новости: CodeCombat не запускается в браузере IPad. Хорошие новости: наше нативное приложение для IPad ожидает одобрения от Apple." campaign: "Кампания" for_beginners: "Новичкам" multiplayer: "Мультиплеер" # Not currently shown on home page for_developers: "Разработчикам" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Или скачайте на iPad" nav: play: "Уровни" # The top nav bar entry where players choose which levels to play @@ -27,7 +28,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi contribute: "Сотрудничество" legal: "Юридическая информация" about: "О нас" - contact: "Контакты" + contact: "Связаться" twitter_follow: "Подписаться" teachers: "Учителям" @@ -56,62 +57,60 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi confirm: "Подтвердить" owned: "Уже есть" # For items you own locked: "Заблокировано" + purchasable: "Можно купить" # For a hero you unlocked but haven't purchased available: "Доступно" skills_granted: "Умение получено" # Property documentation details heroes: "Герои" # Tooltip on hero shop button from /play achievements: "Достижения" # Tooltip on achievement list button from /play account: "Аккаунт" # Tooltip on account button from /play settings: "Настройки" # Tooltip on settings button from /play + poll: "Опрос" # Tooltip on poll button from /play next: "Выбрать" # Go from choose hero to choose inventory before playing a level change_hero: "Выбрать героя" # Go back from choose inventory to choose hero choose_inventory: "Выбрать предметы" buy_gems: "Купить самоцветы" - older_campaigns: "Старые кампании" + subscription_required: "Требуется подписка" anonymous: "Неизвестный игрок" level_difficulty: "Сложность: " campaign_beginner: "Кампания для новичков" - awaiting_levels_adventurer_prefix: "Мы выпускаем по 5 уровней в неделю." + awaiting_levels_adventurer_prefix: "Мы выпускаем новые уровни каждую неделю." awaiting_levels_adventurer: "Зарегистрируйтесь в качестве Искателя приключений" awaiting_levels_adventurer_suffix: "чтобы первым поиграть в новые уровни." - choose_your_level: "Выберите ваш уровень" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Вы можете зайти на любой из этих уровней, а также обсудить уровни на " - adventurer_forum: "форуме Искателей приключений" - adventurer_suffix: "." - campaign_old_beginner: "Старые кампании для новичков" - campaign_old_beginner_description: "... в которой вы познакомитесь с магией программирования." - campaign_dev: "Случайные уровни потруднее" - campaign_dev_description: "... в которых вы изучите интерфейс и научитесь делать кое-что посложнее." + adjust_volume: "Регулировать громкость" campaign_multiplayer: "Арены для мультиплеера" campaign_multiplayer_description: "... в которых вы соревнуетесь в программировании с другими игроками." - campaign_player_created: "Уровни игроков" - campaign_player_created_description: "... в которых вы сражаетесь с креативностью ваших друзей <a href=\"/contribute#artisan\">Ремесленников</a>." - campaign_classic_algorithms: "Классические принципы" - campaign_classic_algorithms_description: "... которые чаще всего встречаются в копьютерных науках." - campaign_forest: "Лесная кампания" - campaign_dungeon: "Кампания в подземелье" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Вы отлично продвигаетесь! Расскажите своим родителям, как много вы уже выучили с CodeCombat." + email_invalid: "Email-адрес некорректен." + form_blurb: "Введите их email-адреса ниже, и мы покажем им!" + form_label: "Email-адрес" + placeholder: "email-адрес" + title: "Прекрасная работа, Ученик" login: sign_up: "Создать аккаунт" log_in: "Войти" logging_in: "Вход..." log_out: "Выйти" - recover: "восстановить аккаунт" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Забыли пароль?" + authenticate_gplus: "Аутентификация G+" + load_profile: "Загрузить профиль G+" + finishing: "Завершение" + sign_in_with_facebook: "Войти через Facebook" + sign_in_with_gplus: "Войти через G+" + signup_switch: "Хотите создать аккаунт?" signup: - create_account_title: "Создать аккаунт, чтобы сохранить прогресс" - description: "Это бесплатно. Нужна лишь пара вещей, и вы сможете продолжить путешествие:" email_announcements: "Получать оповещения по email" - coppa: "Вы старше 13 лет или живёте не в США " - coppa_why: "(почему?)" creating: "Создание аккаунта..." sign_up: "Регистрация" log_in: "вход с паролем" social_signup: "Или вы можете зарегистрироваться через Facebook или G+:" required: "Войдите для того, чтобы продолжить." + login_switch: "Уже есть аккаунт?" recover: recover_account_title: "Восстановить аккаунт" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi books: "Книги" common: + back: "Вернуться" # When used as an action verb, like "Navigate backward" + continue: "Продолжить" # When used as an action verb, like "Continue forward" loading: "Загрузка..." saving: "Сохранение..." sending: "Отправка..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi fork: "Форк" play: "Играть" # When used as an action verb, like "Play next level" retry: "Повторить" + actions: "Действия" + info: "Информация" + help: "Помощь" watch: "Следить" unwatch: "Не следить" submit_patch: "Отослать патч" + submit_changes: "Отослать изменения" + save_changes: "Сохранить изменения" general: and: "и" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi date: "Дата" body: "Содержание" version: "Версия" + pending: "В ожидании" + accepted: "Принято" + rejected: "Отклонено" + withdrawn: "Отозвано" + submitter: "Податель" + submitted: "Представлено" commit_msg: "Сопроводительное сообщение" + review: "Обзор" version_history: "История версий" version_history_for: "История версий для: " + select_changes: "Выберите два изменения ниже, чтобы увидеть различия." + undo_prefix: "Отменить" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Повторить" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Предварительный просмотр текущего уровня" result: "Результат" results: "Результаты" description: "Описание" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi hard: "Сложно" player: "Игрок" player_level: "Уровень" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Воин" + ranger: "Рейнджер" + wizard: "Волшебник" units: second: "секунда" @@ -208,30 +230,30 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi failing: "Неудача" action_timeline: "График действий" click_to_select: "Выберите персонажа, щёлкнув на нём" -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" + control_bar_multiplayer: "Мультиплеер" + control_bar_join_game: "Присоединиться" reload: "Перезагрузить" reload_title: "Перезагрузить код полностью?" reload_really: "Вы уверены, что хотите начать уровень сначала?" reload_confirm: "Перезагрузить всё" + victory: "Победа" victory_title_prefix: "Уровень " victory_title_suffix: " завершён" - victory_sign_up: "Зарегистрироваться" + victory_sign_up: "Зарегистрируйтесь, чтобы сохранить прогресс" victory_sign_up_poke: "Хотите сохранить ваш код? Создайте бесплатный аккаунт!" victory_rate_the_level: "Оцените уровень:" # Only in old-style levels. victory_return_to_ladder: "Вернуться к ладдеру" victory_play_continue: "Продолжить" - victory_play_skip: "Пропустить" - victory_play_next_level: "Следующий уровень" - victory_play_more_practice: "Попрактиковаться еще" - victory_play_too_easy: "Очень легко" - victory_play_just_right: "То, что надо" - victory_play_too_hard: "Очень сложно" victory_saving_progress: "Процесс сохранения" victory_go_home: "На главную" # Only in old-style levels. victory_review: "Расскажите нам больше!" # Only in old-style levels. victory_hour_of_code_done: "Вы закончили?" victory_hour_of_code_done_yes: "Да, я закончил мой Час Кода™!" + victory_experience_gained: "Опыта получено" + victory_gems_gained: "Самоцветов получено" + victory_new_item: "Новый предмет" + victory_viking_code_school: "Ого, это было тяжелый уровень! Если вы еще не разработчик программ, вам стоит им стать. Вы только что ускорири принятие в Школу Викингов, где вы сможете поднять свои навыки на новый уровень и стать профессиональным веб-разработчиком за 14 недель." + victory_become_a_viking: "Станьте Викингом" guide_title: "Руководство" tome_minion_spells: "Заклинания ваших миньонов" # Only in old-style levels. tome_read_only_spells: "Заклинания только для чтения" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi tome_submit_button: "Завершить" tome_reload_method: "Загрузить оригинальный код для этого метода" # Title text for individual method reload button. tome_select_method: "Выбрать метод" - tome_see_all_methods: "Показать все методы, доступные для редактирования" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "Показать все методы, доступные для редактирования" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Выбрать кого-нибудь для " tome_available_spells: "Доступные заклинания" tome_your_skills: "Ваши навыки" + tome_help: "Помощь" tome_current_method: "Текущий метод" hud_continue_short: "Продолжить" code_saved: "Код сохранен" @@ -253,17 +276,24 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi keyboard_shortcuts: "Горячие клавиши" loading_ready: "Готово!" loading_start: "Начать уровень" - problem_alert_title: "Исправьте Ваш Код" + problem_alert_title: "Исправьте ваш Код" + problem_alert_help: "Помощь" time_current: "Текущее:" time_total: "Максимальное:" time_goto: "Перейти на:" + non_user_code_problem_title: "Невозможно загрузить уровень" + infinite_loop_title: "Обнаружен бесконечный цикл" + infinite_loop_description: "Код сотворения мира не завершил выполнение. Это могло случиться из-за реально медленного кода или наличия бесконечного цикла. Или там может быть баг. Вы можете попытаться запустить этот код еще раз или сбросить код в состояние по умолчанию. Если проблема не будет решена, дайте нам знать." + check_dev_console: "Вы так же можете открыть консоль разработчика, чтобы увидеть, что может идти не так." + check_dev_console_link: "(инструкции)" infinite_loop_try_again: "Попробовать снова" infinite_loop_reset_level: "Сбросить уровень" infinite_loop_comment_out: "Закомментировать мой код" tip_toggle_play: "Переключайте воспроизведение/паузу комбинацией Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ и Ctrl+] - перемотка назад и вперёд." - tip_guide_exists: "Щёлкните \"руководство\" наверху страницы для получения полезной информации." + tip_scrub_shortcut: "Используйте Ctrl+[ и Ctrl+] для перемотки назад и вперёд." + tip_guide_exists: "Щёлкните руководство внутри меню (наверху страницы) для получения полезной информации." tip_open_source: "Исходный код CodeCombat открыт на 100%!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat запустил бета-тестирование в октябре 2013 года." tip_think_solution: "Думайте о решении, а не о проблеме." tip_theory_practice: "В теории, между практикой и теорией нет разницы. Но на практике есть. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi tip_hofstadters_law: "Закон Хофштадтера: Любое дело всегда длится дольше, чем ожидается, даже если учесть закон Хофштадтера." tip_premature_optimization: "Поспешная оптимизация - корень всех зол. - Donald Knuth" tip_brute_force: "Когда сомневаешься используй грубую силу. - Кен Томпсон" - customize_wizard: "Настройки волшебника" + tip_extrapolation: "Есть два типа людей: те, кто могут экстраполировать неполные данные..." + tip_superpower: "Программирование делает нас ближе к обретению суперсилы." + tip_control_destiny: "В настоящей среде открытого кода у вас есть право контролировать свою судьбу. - Linus Torvalds" + tip_no_code: "Нет кода быстрее, чем его отсутствие." + tip_code_never_lies: "Код никогда не врет. Комментарии - иногда. — Ron Jeffries" + tip_reusable_software: "Прежде, чем программное обеспечение станет повторно используемым, оно должно стать в принципе используемым." + tip_optimization_operator: "В каждом языке есть оператор оптимизации. В большинстве языков это оператор ‘//’" + tip_lines_of_code: "Измерение прогресса программирования в строках кода - это как измерять прогресс построения самолета по его весу. — Bill Gates" + tip_source_code: "Я хочу изменить мир, но они вряд ли дадут мне исходники." + tip_javascript_java: "Java к JavaScript относится так же, как кол относится к колготкам. - Chris Heilmann (перефраз.)" + tip_move_forward: "Что бы вы ни делали, вы должны двигаться вперед. - Martin Luther King Jr" + tip_google: "У вас проблема, которую вы не можете решить? Гуглите!" + tip_adding_evil: "Добавим щепотку зла." + tip_hate_computers: "Есть одна вещь в людях, которые думают, что они ненавидят компьютеры. Что они на самом деле ненавидят, так это плохих программистов. - Larry Niven" + tip_open_source_contribute: "Вы можете помочь сделать CodeCombat лучше!" + tip_recurse: "Итерация свойственна человеку, рекурсия божественна. - L. Peter Deutsch" + tip_free_your_mind: "Отвлекись от всего, Нео. Страх, неверие, сомнения отбрось — очисти свой разум. - Morpheus" + tip_strong_opponents: "Даже сильнейший противник имеет слабость. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Инвентарь" save_load_tab: "Сохранить/Загрузить" options_tab: "Настройки" guide_tab: "Руководство" + guide_video_tutorial: "Видео-учебник" + guide_tips: "Заметки" multiplayer_tab: "Мультиплеер" auth_tab: "Зарегистрироваться" inventory_caption: "Оденьте своего героя" @@ -306,25 +356,114 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi multiplayer_caption: "Играй с друзьями!" auth_caption: "Сохранить прогресс." + leaderboard: + leaderboard: "Таблица лидеров" + view_other_solutions: "Посмотреть Таблицу лидеров" + scores: "Рейтинг" + top_players: "Сортировать игроков по" + day: "Сегодня" + week: "На этой неделе" + all: "За все время" + time: "Время" + damage_taken: "Получено повреждений" + damage_dealt: "Нанесено повреждений" + difficulty: "Сложность" + gold_collected: "Собрано золота" + inventory: choose_inventory: "Выбрать предметы" equipped_item: "Выбранный" + required_purchase_title: "Необходимо" available_item: "Доступно" -# restricted_title: "Restricted" - should_equip: "(двойной клик чтобы одеть)" + restricted_title: "Ограничено" + should_equip: "(двойной клик чтобы надеть)" equipped: "(выбранный)" locked: "(заблокированный)" restricted: "(запрещен в этом уровне)" -# equip: "Equip" -# unequip: "Unequip" + equip: "Надеть" + unequip: "Снять" buy_gems: few_gems: "Немного самоцветов" pile_gems: "Кучка самоцветов" chest_gems: "Сундук с самоцветами" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + purchasing: "Покупка..." + declined: "Ваша карта отклонена" + retrying: "Ошибка сервера, пробуем еще раз." + prompt_title: "Не хватает самоцветов" + prompt_body: "Хотите еще?" + prompt_button: "Войти в магазин" + recovered: "Предыдущие покупки самоцветов восстановлены. Пожалуйста, обновите страницу." + price: "x3500 / месяц" + + subscribe: + comparison_blurb: "Отточите свое мастерство благодаря подписке на CodeCombat!" + feature1: "80+ основных уровней на просторах 4-х миров" # {change} + feature2: "7 могущественных <strong>новых героев</strong> с уникальными способностями!" # {change} + feature3: "60+ дополнительных уровней" # {change} + feature4: "<strong>3500 бонусных самоцветов</strong> каждый месяц!" + feature5: "Обучающие видеоролики" + feature6: "Эксклюзивная поддержка по электронной почте" + feature7: "Частные <strong>Кланы</strong>" + free: "Бесплатно" + month: "месяц" + subscribe_title: "Подпишись" + unsubscribe: "Отписаться" + confirm_unsubscribe: "Подтвердить отмену подписки" + never_mind: "Неважно, Я Все Равно Тебя Люблю" + thank_you_months_prefix: "Спасибо вам за поддерживание нас в течение последних" + thank_you_months_suffix: "месяцев." + thank_you: "Спасибо за поддержку CodeCombat." + sorry_to_see_you_go: "Жаль, что вы уходите! Пожалуйста, расскажите нам, что мы могли бы сделать лучше." + unsubscribe_feedback_placeholder: "О, что мы наделали?" + parent_button: "Спросить у родителей" + parent_email_description: "Мы отправим им электронное письмо, чтобы они смогли приобрести тебе подписку на CodeCombat." + parent_email_input_invalid: "Адрес электронной почты введен неправильно." + parent_email_input_label: "Адрес электронной почты родителей" + parent_email_input_placeholder: "Введи адрес электронной почты родителей" + parent_email_send: "Отправить письмо" + parent_email_sent: "Письмо отправлено!" + parent_email_title: "Какой у твоих родителей адрес электронной почты?" + parents: "Для Родителей" + parents_title: "Дорогой Родитель, ваш ребенок учится программировать. Вы поможете ему?" + parents_blurb1: "Ваш ребенок прошел уже __nLevels__ уровней и выучил основы программирования. Помогите развить их интерес и купите им подписку, чтобы они могли продолжить играть." + parents_blurb1a: "Программирование - это существенный навык, который ваш ребенок несомненно будет использовать, когда станет взрослым. В 2020, базовые навыки работы с программным обеспечением будут необходимы для 77% профессий, а программные инженеры пользуются высоким спросом по всему миру. Вы знали, что компьютерная наука - это наиболее оплачиваемая специальность?" + parents_blurb2: "За $9.99 USD/месяц ваш ребенок получит новые испытания каждую неделю и персональную поддержку профессиональных программистов через электронную почту." + parents_blurb3: "Без риска: 100% гарантия возврата денег, возможность отписаться в 1 клик." + payment_methods: "Методы оплаты" + payment_methods_title: "Принимаемые методы оплаты" + payment_methods_blurb1: "На данный момент мы принимаем кредитные карты и Alipay." + payment_methods_blurb2: "Если вам необходим альтернативный способ оплаты, пожалуйста, свяжитесь" + stripe_description: "Месячная подписка" + subscription_required_to_play: "Чтобы сыграть этот уровень нужна подписка." + unlock_help_videos: "Подпишитесь, чтобы разблокировать все обучающие видео." + personal_sub: "Личная подписка" # Accounts Subscription View below + loading_info: "Загружаем информацию о подписке..." + managed_by: "Управляется" + will_be_cancelled: "Будет отменена" + currently_free: "Сейчас вы имеете бесплатную подписку" + currently_free_until: "Сейчас вы имеете бесплатную подписку до" + was_free_until: "Вы имели бесплатную подписку до" + managed_subs: "Управляемые подписки" + managed_subs_desc: "Добавьте подписки для других игроков (студенты, дети и т.д.)" + managed_subs_desc_2: "Получатели должны иметь аккаунт CodeCombat, связанный с email-адресом, указанным вами." + group_discounts: "Групповые скидки" + group_discounts_1: "Так же мы предлагаем групповые скидки для нескольких подписок." + group_discounts_1st: "1-я подписка" + group_discounts_full: "Полная стоимость" + group_discounts_2nd: "Подписки со 2-й по 11-ю" + group_discounts_20: "20% скидка" + group_discounts_12th: "Подписки 12-я и больше" + group_discounts_40: "40% скидка" + subscribing: "Подписываемся..." + recipient_emails_placeholder: "Введите email-адреса для подписки, по одному на каждой линии." + subscribe_users: "Подписать пользователей" + users_subscribed: "Подписанные пользователи:" + no_users_subscribed: "Нет подписанных пользователей. Пожалуйста, проверьте список email-адресов еще раз." + current_recipients: "Текущие получатели" + unsubscribing: "Отписываемся..." + subscribe_prepaid: "Нажмите 'Подпишись', чтобы использовать предоплаченный код" + using_prepaid: "Использовать предоплаченный код для месячной подписки" choose_hero: choose_hero: "Выберите героя" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi lua_blurb: "Скриптовый язык для игр." io_blurb: "Простой, но непонятный." status: "Статус" + hero_type: "Тип" weapons: "Оружие" weapons_warrior: "Меч - ближний бой, не магический" weapons_ranger: "Арбалеты, ружья - дальнобойные, не магические" @@ -349,25 +489,37 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi regeneration: "Регенерация" range: "Зона" # As in "attack or visual range" blocks: "Блокирует" # As in "this shield blocks this much damage" + backstab: "Со спины" # As in "this dagger does this much backstab damage" skills: "Умения" + attack_1: "Наносит" + attack_2: "от указанного" + attack_3: "урона оружия." + health_1: "Получает" + health_2: "от указанного" + health_3: "здоровья." + speed_1: "Передвигается со скоростью" + speed_2: "метров в секунду." + available_for_purchase: "Доступно для покупки" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Разблокируется на уровне:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Только определенные герои могут играть на этом уровне." -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + skill_docs: + writable: "доступно для записи" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "только чтение" + action_name: "имя" + action_cooldown: "Применяется" + action_specific_cooldown: "Восстановление" + action_damage: "Повреждения" + action_range: "Дальность" + action_radius: "Радиус" + action_duration: "Длительность" + example: "Пример" + ex: "пр." # Abbreviation of "example" + current_value: "Текущее значение" + default_value: "Значение по умолчанию" + parameters: "Параметры" + returns: "Возвращает" + granted_by: "Предоставлено" save_load: granularity_saved_games: "Сохранено" @@ -378,8 +530,6 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi volume_label: "Громкость" music_label: "Музыка" music_description: "Фоновая музыка вкл/выкл" - autorun_label: "Автозапуск" - autorun_description: "Настройка автоматического выполнения кода." editor_config: "Настройки редактора" editor_config_title: "Настройки редактора" editor_config_level_language_label: "Язык для этого уровня" @@ -402,47 +552,141 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi why_codecombat: "Почему CodeCombat?" why_paragraph_1: "Нужно научиться программировать? Вам не нужны уроки. Вам нужно написать много кода и прекрасно провести время, делая это." why_paragraph_2_prefix: "Вот где программирование. Это должно быть весело. Не забавно, вроде" - why_paragraph_2_italic: "вау, значок," + why_paragraph_2_italic: "\"вау, значок\"," why_paragraph_2_center: "а" - why_paragraph_2_italic_caps: "НЕТ, МАМ, Я ДОЛЖЕН ПРОЙТИ УРОВЕНЬ!" + why_paragraph_2_italic_caps: "\"НЕТ, МАМ, Я ДОЛЖЕН ПРОЙТИ УРОВЕНЬ!\"" why_paragraph_2_suffix: "Вот, почему CodeCombat - мультиплеерная игра, а не курс уроков в игровой форме. Мы не остановимся, пока вы не потеряете голову - в данном случае, это хорошо." why_paragraph_3: "Если вы собираетесь увлечься какой-нибудь игрой, увлекитесь этой и станьте одним из волшебников века информационных технологий." press_title: "Блогерам/Прессе" press_paragraph_1_prefix: "Хотите написать о нас? Скачивайте и используйте все ресурсы, включенный в наш" press_paragraph_1_link: "пресс-пакет" press_paragraph_1_suffix: ". Все изображения могут быть использованы без предварительного уведомления." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + team: "Команда" + george_title: "Сооснователь" + george_blurb: "Деловой" + scott_title: "Сооснователь" + scott_blurb: "Разумный" + nick_title: "Сооснователь" + nick_blurb: "Гуру мотивации" + michael_title: "Программист" + michael_blurb: "Сисадмин" + matt_title: "Сооснователь" + matt_blurb: "Велосипедист" + cat_title: "Главный ремесленник" + cat_blurb: "Повелитель стихий" + josh_title: "Дизайнер игры" + josh_blurb: "Пол - это лава" + jose_title: "Музыка" + jose_blurb: "На взлет" + retrostyle_title: "Иллюстрирование" + retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat: Информация для учителей" + intro_1: "CodeCombat - это онлайн игра, которая обучает программированию. Ученики пишут код на реальных языках программирования." + intro_2: "Опыт не обязателен!" + free_title: "Сколько это стоит?" + cost_china: "Первые 5 уровней CodeCombat бесплатны в Китае. При оплате $9.99 в месяц вы получите доступ к последующим более чем 140 уровням на наших эксклюзивных серверах в Китае." + free_1: "В наличии более 80 БЕСПЛАТНЫХ уровней, которые покрывают каждый концепт." # {change} + free_2: "Месячная подписка предоставляет доступ к видео-урокам и дополнительным уровням." + teacher_subs_title: "Учителя получают бесплатные подписки!" + teacher_subs_1: "Пожалуйста, заполните нашу" + teacher_subs_2: "Анкету учителя" + teacher_subs_3: "для настройки вашей подписки." + sub_includes_title: "Что включено в подписку?" + sub_includes_1: "В дополнение к более чем 80 бесплатным уровням ученики с месячной подпиской получат доступ к дополнительным возможностям:" # {change} + sub_includes_2: "Более 60 уровней для дополнительной практики" # {change} + sub_includes_3: "Видео-уроки" + sub_includes_4: "Эксклюзивная поддержка по электронной почте" + sub_includes_5: "7 новых героев с уникальными возможностями для оттачивания мастерства" # {change} + sub_includes_6: "3500 бонусных самоцветов каждый месяц" + sub_includes_7: "Частные Кланы" + monitor_progress_title: "Как мне следить за прогрессом студентов?" + monitor_progress_1: "Прогресс студентов может быть отследить, создав" + monitor_progress_2: "для вашего класса." + monitor_progress_3: "Чтобы добавить студентов, отправьте им ссылку-пришлашение в ваш Клан, которую можно найти на странице" + monitor_progress_4: "." + monitor_progress_5: "После того, как они присоединятся, вы увидите сводку по прогрессам студентов на странице вашего Клана." + private_clans_1: "Частные Кланы обеспечивают повышенную конфиденциальность и детальную информацию по прогрессу каждого студента." + private_clans_2: "Чтобы создать частный Клан, отметьте флажок 'Сделать клан частным', когда будете создавать" + private_clans_3: "." + who_for_title: "Для кого предназначен CodeCombat?" + who_for_1: "Мы рекомендуем CodeCombat для учеников старше 9 лет. Какой-либо опыт программирования не требуется." + who_for_2: "Мы разработали CodeCombat так, чтобы он подходил и мальчикам и девочкам." + material_title: "Как много здесь материала?" + material_china: "Около 30 часов игрового процесса, распределенного более чем на 140 уровней для подписчиков с добавлением новых уровней каждую неделю." # {change} + material_1: "Около 10 часов бесплатного контента и 20 часов дополнительного контента для подписчиков с добавлением новых уровней каждую неделю." # {change} + concepts_title: "О каких концептах мы рассказываем?" + how_much_title: "Сколько стоит месячная подписка?" + how_much_1: "Цена" + how_much_2: "месячной подписки" + how_much_3: "- $9.99. Подписка может быть отменена в любой момент." + how_much_4: "Дополнительно мы предоставляем скидки для больших групп:" + how_much_5: "Мы предлагаем скидки для разовых покупок и годовых подписок для групп, таких как класс или школа. Пожалуйста, напишите на" + how_much_6: "для получения подробностей." + more_info_title: "Где я могу найти больше информации?" + more_info_1: "Наш" + more_info_2: "форум учителей" + more_info_3: "хорошее место для связи с другими педагогами, уже использующими CodeCombat." + sys_requirements_title: "Системные требования" + sys_requirements_1: "Современный веб-браузер. Последние версии Chrome, Firefox или Safari. Internet Explorer 9 или новее." + sys_requirements_2: "CodeCombat пока не доступен на iPad." + + teachers_survey: + title: "Анкета учителя" + must_be_logged: "Сначала необходимо войти в аккаунт. Пожалуйста, создайте аккаунт или войдите через меню вверху." + retrieving: "Получение информации..." + being_reviewed_1: "Ваша заявка на бесплатную пробную подписку сейчас" + being_reviewed_2: "на рассмотрении." # {change} + approved_1: "Ваша заявка на бесплатную пробную подписку была" + approved_2: "утверждена." # {change} + approved_3: "Дальнейшие инструкции были высланы на" + denied_1: "Ваша заявка на бесплатную пробную подписку была" + denied_2: "отклонена." # {change} + contact_1: "Пожалуйста, свяжитесь с" + contact_2: ", если у вас остались вопросы." + description_1: "Мы предлагаем бесплатные подписки учителям в целях ознакомления. Вы можете найти больше информации на нашей странице" + description_2: "учителей" + description_3: "" + description_4: "Пожалуйста, заполните эту маленькую анкету и мы вышлем вам инструкции по установке на email." + email: "Email-адрес" + school: "Наименование школы" + location: "Наименование города" + age_students: "Сколько лет вашим студентам?" + under: "Меньше" + other: "Другое:" + amount_students: "Как много студентов вы обучаете?" + hear_about: "Как вы узнали о CodeCombat?" + fill_fields: "Пожалуйста, заполните все поля." + thanks: "Спасибо! Скоро мы вышлем вам инструкцию по установке." versions: save_version_title: "Сохранить новую версию" new_major_version: "Новая основная версия" + submitting_patch: "Отправка патча..." cla_prefix: "Чтобы сохранить изменения, сначала вы должны согласиться с нашим" cla_url: "лицензионным соглашением соавторов" cla_suffix: "." cla_agree: "Я СОГЛАСЕН" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Связаться с CodeCombat" welcome: "Мы рады вашему сообщению! Используйте эту форму, чтобы отправить нам email. " - contribute_prefix: "Если вы хотите внести свой вклад в проект, зайдите на нашу " - contribute_page: "страницу сотрудничества" - contribute_suffix: "!" forum_prefix: "Для любых публичных обсуждений, пожалуйста, используйте " forum_page: "наш форум" forum_suffix: "." + faq_prefix: "Так же у нас есть" + faq: "FAQ" + subscribe_prefix: "Если вам нужна помощь в прохождении уровня, пожалуйста," + subscribe: "купите подписку CodeCombat," + subscribe_suffix: "и мы будем рады помочь вам с вашим кодом." + subscriber_support: "Ваши электронные письма получают нашу приоритетную поддержку, если вы подписчик CodeCombat." + screenshot_included: "Скриншот прилагается." + where_reply: "Куда мы должны ответить?" send: "Отправить отзыв" contact_candidate: "Связаться с кандидатом" # Deprecated - recruitment_reminder: "Используйте эту форму, чтобы обратиться к кандидатам, если вы заинтересованы в интервью. Помните, что CodeCombat взимает 18% от первого года зарплаты. Плата производится по найму сотрудника и подлежит возмещению в течение 90 дней, если работник не остаётся на рабочем месте. Работники с частичной занятостью, работаюие удалённо и по контракту свободны, как стажёры." # Deprecated + recruitment_reminder: "Используйте эту форму, чтобы обратиться к кандидатам, если вы заинтересованы в интервью. Помните, что CodeCombat взимает 18% от первого года зарплаты. Плата производится по найму сотрудника и подлежит возмещению в течение 90 дней, если работник не остаётся на рабочем месте. Работники с частичной занятостью, работающие удалённо и по контракту свободны, как стажёры." # Deprecated account_settings: title: "Настройки аккаунта" @@ -450,12 +694,19 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi autosave: "Настройки сохраняются автоматически" me_tab: "Я" picture_tab: "Аватар" + delete_account_tab: "Удалить ваш аккаунт" + wrong_email: "Неверный email" + wrong_password: "Неверный пароль" upload_picture: "Загрузить изображение" + delete_this_account: "Удалить этот аккаунт навсегда" + god_mode: "Режим бога" password_tab: "Пароль" emails_tab: "Email-адреса" admin: "Админ" new_password: "Новый пароль" new_password_verify: "Подтверждение пароля" + type_in_email: "Введите ваш email для подтверждения удаления аккаунта" # {change} + type_in_password: "Так же введите ваш пароль" # {change} email_subscriptions: "Email-подписки" email_subscriptions_none: "Нет активных подписок." email_announcements: "Оповещения" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi job_profile_explanation: "Привет! Заполните это, и мы свяжемся с вами при нахождении работы разработчика программного обеспечения для вас." sample_profile: "Посмотреть пример профиля" view_profile: "Просмотр вашего профиля" - wizard_tab: "Волшебник" - wizard_color: "Цвет одежды волшебника" keyboard_shortcuts: keyboard_shortcuts: "Горячие клавиши" space: "Пробел" enter: "Enter" + press_enter: "нажмите enter" escape: "Escape" shift: "Shift" run_code: "Выполнить текущий код." @@ -496,46 +746,95 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi skip_scripts: "Пропустить все возможные скрипты." toggle_playback: "Переключить проигрывание/паузу." scrub_playback: "Перемотка назад и вперед во времени." - single_scrub_playback: "Scrub back and forward through time by a single frame." - scrub_execution: "Scrub through current spell execution." + single_scrub_playback: "Покадровая перемотка назад и вперед." + scrub_execution: "Перескочить через выполнение текущего заклинания." toggle_debug: "Включить отображение отладки." toggle_grid: "Включить наложение сетки." toggle_pathfinding: "Включить путевой оверлей.." beautify: "Приукрасьте свой код стандартизацией его форматирования." maximize_editor: "Развернуть/свернуть редактор кода." - move_wizard: "Перемещайте своего Волшебника по уровню." community: main_title: "Сообщество CodeCombat" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + introduction: "Просмотрите ниже способы втянуться и решите, что для вас звучит наиболее весело и интересно. Мы с нетерпением ожидаем совместной работы с вами!" + level_editor_prefix: "Используйте" + level_editor_suffix: "CodeCombat чтобы создавать и редактировать уровни. Пользователи могут создавать уровни для своих одноклассников, друзей, хакатонов, студентов, братьев и сестер. Если создание нового уровня звучит устрашающе, вы можете начать, ответвившись от одного из наших!" + thang_editor_prefix: "Мы называем единицы внутри игры 'предметами'. Используйте" + thang_editor_suffix: "чтобы изменять ресурсы игры. Позволяйте единицам бросать снаряды, измените направление анимации, очки здоровья или загрузите свои собственные векторные изображения." + article_editor_prefix: "Увидели ошибку в нашей документации? Хотите сделать инструкции к вашим творениям? Посмотрите" + article_editor_suffix: "и помогите игрокам CodeCombat получить максимум от их времяпрепровождения." + find_us: "Найдите нас на этих сайтах" + social_blog: "Читайте блог CodeCombat на Sett" + social_discource: "Присоединяйтесь к обсуждению на нашем форуме" + social_facebook: "Оцените CodeCombat на Facebook" + social_twitter: "Следуйте за CodeCombat на Twitter" + social_gplus: "Присоединяйтесь к CodeCombat на Google+" + social_hipchat: "Общайтесь с нами в публичной чат-комнате CodeCombat на HipChat" + contribute_to_the_project: "Сотрудничайте с проектом" + + clans: + clan: "Клан" + clans: "Кланы" + new_name: "Имя нового клана" + new_description: "Описание нового клана" + make_private: "Сделать клан частным" + subs_only: "только для подписчиков" + create_clan: "Создать новый клан" +# private_preview: "Preview" + public_clans: "Публичные кланы" + my_clans: "Мои кланы" + clan_name: "Имя клана" + name: "Имя" + chieftain: "Вождь" + type: "Тип" + edit_clan_name: "Редактировать имя клана" + edit_clan_description: "Редактировать описание клана" + edit_name: "редактировать имя" + edit_description: "редактировать описание" + private: "(частный)" + summary: "Сводка" + average_level: "Средний уровень" + average_achievements: "Среднее количество достижений" + delete_clan: "Удалить клан" + leave_clan: "Покинуть клан" + join_clan: "Присоединиться к клану" + invite_1: "Пригласить:" + invite_2: "*Пригласите игроков в этот клан, отправив им эту ссылку." + members: "Участники" + progress: "Прогресс" + not_started_1: "не начат" + started_1: "начат" + complete_1: "завершен" + exp_levels: "Раскрыть уровни" + rem_hero: "Удалить героя" + status: "Статус" + complete_2: "Завершен" + started_2: "Начат" + not_started_2: "Не начат" + view_solution: "Нажмите, чтобы увидеть решение." + latest_achievement: "Последнее достижение" + playtime: "Время игры" + last_played: "Последняя игра" classes: archmage_title: "Архимаг" archmage_title_description: "(программист)" + archmage_summary: "Если вы разработчик, заинтересованный в программировании обучающих игр, становитесь архимагом, чтобы помогать творить CodeCombat!" artisan_title: "Ремесленник" artisan_title_description: "(создатель уровней)" + artisan_summary: "Создавайте и делитесь уровнями, на которых сможете играть вы и ваши друзья. Становитесь ремесленником и овладейте искусством обучения программированию других." adventurer_title: "Искатель приключений" adventurer_title_description: "(тестировщик уровней)" + adventurer_summary: "Получайте наши новые уровни (даже контент для подписчиков) бесплатно на неделю раньше и помогите нам с работой над исправлением багов до публичного релиза." scribe_title: "Писарь" scribe_title_description: "(редактор статей)" + scribe_summary: "Хороший код требует хорошую документацию. Пишите, редактируйте и улучшайте документацию, читаемую миллионами игроков по всему миру." diplomat_title: "Дипломат" diplomat_title_description: "(переводчик)" + diplomat_summary: "CodeCombat переводится нашими Дипломатами на более чем 45 языков. Помогите нам и посодействуйте в переводах." ambassador_title: "Посол" ambassador_title_description: "(поддержка)" + ambassador_summary: "Приручите наших пользователей форума и давайте рекомендации по их вопросам. Наши Послы представляют CodeCombat миру." editor: main_title: "Редакторы CodeCombat" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi thang_title: "Редактор объектов" level_title: "Редактор уровней" achievement_title: "Редактор достижений" + poll_title: "Редактор опросов" back: "Назад" revert: "Откатить" revert_models: "Откатить Модели" pick_a_terrain: "Выберите ландшафт" - small: "Маленький" + dungeon: "Подземелье" + indoor: "Комнатный" + desert: "Пустыня" grassy: "Травянистый" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Маленький" + large: "Большой" fork_title: "Форк новой версии" fork_creating: "Создание форка..." generate_terrain: "Создать ландшафт" more: "Ещё" wiki: "Вики" live_chat: "Онлайн-чат" + thang_main: "Главное" + thang_spritesheets: "Таблицы спрайтов" + thang_colors: "Цвета" level_some_options: "Ещё опции" level_tab_thangs: "Объекты" level_tab_scripts: "Скрипты" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi level_tab_thangs_all: "Все" level_tab_thangs_conditions: "Начальные условия" level_tab_thangs_add: "Добавить объект" +# level_tab_thangs_search: "Search thangs" + add_components: "Добавить компоненты" + component_configs: "Конфигурации компонентов" + config_thang: "Двойной клик для конфигурирования объектов" delete: "Удалить" duplicate: "Дублировать" + stop_duplicate: "Остановить дублирование" rotate: "Повернуть" level_settings_title: "Настройки" level_component_tab_title: "Текущие компоненты" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi new_level_title_login: "Войти, чтобы создать новый уровень" new_achievement_title: "Создать новое достижение" new_achievement_title_login: "Войти, чтобы создать новое достижение" + new_poll_title: "Создать новый опрос" + new_poll_title_login: "Войдите, чтобы создать новый опрос" article_search_title: "Искать статьи" thang_search_title: "Искать типы объектов" level_search_title: "Искать уровни" achievement_search_title: "Искать достижения" + poll_search_title: "Искать опросы" read_only_warning2: "Примечание: вы не можете сохранять любые правки здесь, потому что вы не авторизованы." no_achievements: "Для этого уровня еще не были добавлены достижения." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" -# pop_i18n: "Populate I18N" + achievement_query_misc: "Ключевое достижение за прочие задачи." + achievement_query_goals: "Ключевое достижение за цели уровня" + level_completion: "Выполнение уровня" + pop_i18n: "Популяризируйте I18N" + tasks: "Задачи" + clear_storage: "Очистить ваши локальные изменения" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Предпросмотр" edit_article_title: "Редактирование статьи" + polls: + priority: "Приоритет" + contribute: page_title: "Сотрудничество" - character_classes_title: "Классы персонажей" - introduction_desc_intro: "Мы возлагаем большие надежды на CodeCombat." - introduction_desc_pref: "Мы хотим быть местом, где программисты всех мастей приходят учиться и играть вместе, знакомить остальных с удивительным миром программирования, и отражают лучшие части сообщества. Мы не можем и не хотим этого делать в одиночку; то, что делает такие проекты, как GitHub, Stack Overflow и Linux великими - люди, которые их используют и создают на их основе. С этой целью " - introduction_desc_github_url: "исходный код CodeCombat полностью открыт" - introduction_desc_suf: ", и мы стремимся предоставить как можно больше способов, чтобы вы могли принять участие и сделать этот проект настолько же вашим, как и нашим." - introduction_desc_ending: "Мы надеемся, что вы присоединитесь к нашей команде!" - introduction_desc_signature: "- Ник, Джордж, Скотт, Михаэль, Джереми и Глен" + intro_blurb: "CodeCombat на 100% открыт! Сотни преданных игроков помогли нам превратить игру в то, чем она является сегодня. Присоединяйтесь к нам и напишите следующую главу в миссии CodeCombat научить мир программировать!" alert_account_message_intro: "Привет!" alert_account_message: "Чтобы подписаться на классовые сообщения, необходимо войти в аккаунт" - archmage_summary: "Интересует работа над игровой графикой, дизайном пользовательского интерфейса, базой данных и организацией сервера, сетевым мультиплеером, физикой, звуком или производительностью игрового движка? Хотите помочь создать игру для помощи другим людям в изучении того, в чём вы хорошо разбираетесь? У нас много работы, и если вы опытный программист и хотите разрабатывать для CodeCombat, этот класс для вас. Мы будем рады вашей помощи в создании самой лучшей игры для программистов." archmage_introduction: "Одна из лучших черт в создании игр - то, что они синтезируют так много различных вещей. Графика, звук, сетевое взаимодействие в режиме реального времени, социальное сетевое взаимодействие, и, конечно, большинство из более распространённых аспектов программирования, от низкоуровневого управления базами данных и администрирования сервера до построения дизайна и интерфейсов, видимых пользователю. У нас много работы, и если вы опытный программист со страстным желанием погрузиться в действительно мельчайшие детали CodeCombat, этот класс для вас. Мы будем рады вашей помощи в создании самой лучшей игры для программистов." class_attributes: "Атрибуты класса" archmage_attribute_1_pref: "Знания о " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi join_desc_4: "и мы решим, откуда можно начать!" join_url_email: "Напишите нам" join_url_hipchat: "публичной комнате HipChat" - more_about_archmage: "Узнать больше о том, как стать Архимагом" archmage_subscribe_desc: "Получать email-ы о новых возможностях для программирования и объявления." - artisan_summary_pref: "Хотите проектировать уровни и расширить арсенал CodeCombat? Люди проходят наш контент на порядок быстрее, чем мы его создаём! В данный момент, наш редактор уровней только скелет, так что будьте осторожны. Создание уровней будет немного сложным и глючным. Если у вас есть видение кампаний, связывающих циклы for в" - artisan_summary_suf: ", тогда этот класс для вас." artisan_introduction_pref: "Мы должны строить дополнительные уровни! Люди будут требовать больше контента и создавать его можем только мы сами. Сейчас ваша рабочая станция первого уровня; наш редактор уровней едва пригоден для использования создателями, так что будьте осторожны. Если у вас есть видение кампаний, связывающих циклы for в" artisan_introduction_suf: ", тогда этот класс для вас." artisan_attribute_1: "Любой опыт по созданию подобного контента был бы хорош, например, использование редакторов уровней Blizzard. Но не обязателен!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi artisan_join_step2: "Создайте новый уровень и изучите существующие уровни." artisan_join_step3: "Найдите нас в нашей публичной комнате HipChat для помощи." artisan_join_step4: "Разместите свои уровни на форуме для обратной связи." - more_about_artisan: "Узнать больше о том, как стать Ремесленником" artisan_subscribe_desc: "Получать email-ы об обновлениях редактора уровней и объявления." - adventurer_summary: "Позвольте внести ясность о вашей роли: вы танк. Вы собираетесь принять тяжелые повреждения. Нам нужны люди, чтобы испытать совершенно новые уровни и помочь определить, как сделать лучше. Боль будет огромной; создание хороших игр - длительный процесс и никто не делает это правильно в первый раз. Если вы можете выдержать и имеете высокий балл конституции (D&D), этот класс для вас." adventurer_introduction: "Позвольте внести ясность о вашей роли: вы танк. Вы собираетесь принять тяжелые повреждения. Нам нужны люди, чтобы испытать совершенно новые уровни и помочь определить, как сделать лучше. Боль будет огромной; создание хороших игр - длительный процесс и никто не делает это правильно в первый раз. Если вы можете выдержать и имеете высокий балл конституции (D&D), этот класс для вас." adventurer_attribute_1: "Жажда обучения. Вы хотите научиться программировать и мы хотим научить вас программировать. Вы, вероятно, проведёте большую часть обучения в процессе." adventurer_attribute_2: "Харизматичность. Будьте нежны, но ясно формулируйте, что нуждается в улучшении и вносите свои предложения по улучшению." adventurer_join_pref: "Либо объединитесь (или наймите!) с Ремесленником и работайте с ним, или установите флажок ниже для получения email-ов, когда появляются новые уровни для тестирования. Также мы будем размещать записи об уровнях для обзора в наших сетях, таких, как" adventurer_forum_url: "наш форум" adventurer_join_suf: "поэтому, если вы предпочитаете получать уведомления таким способом, зарегистрируйтесь там!" - more_about_adventurer: "Узнать больше о том, как стать Искателем приключений" adventurer_subscribe_desc: "Получать email-ы при появлении новых уровней для тестирования." - scribe_summary_pref: "CodeCombat будет не просто кучей уровней. Он также будет ресурсом знаний в области программирования, к которому игроки могут присоединиться. Таким образом, каждый Ремесленник может ссылаться на подробную статью для назидания игрока: документация сродни тому, что создана " - scribe_summary_suf: ". Если вам нравится объяснять концепции программирования, этот класс для вас." scribe_introduction_pref: "CodeCombat будет не просто кучей уровней. Он также включает в себя ресурс для познания, вики концепций программирования, которые уровни могут включать. Таким образом, вместо того, чтобы каждому Ремесленнику необходимо было подробно описывать, что такое оператор сравнения, они могут просто связать их уровень с уже написанной в назидание игрокам статьёй, описывающей их. Что-то по аналогии с " - scribe_introduction_url_mozilla: "Mozilla Developer Network" + scribe_introduction_url_mozilla: "Сеть Разработчиков Mozilla" scribe_introduction_suf: ". Если ваше представление о веселье это формулирование концепций программирования в форме Markdown, этот класс для вас." scribe_attribute_1: "Навык в письме - в значительной степени всё, что вам нужно. Не только грамматика и правописание, но и способность передать сложные идеи другим." contact_us_url: "Свяжитесь с нами" scribe_join_description: "расскажите нам немного о себе, вашем опыте в программировании и какие вещи вы хотели бы описывать. Отсюда и начнём!" - more_about_scribe: "Узнать больше о том, как стать Писарем" scribe_subscribe_desc: "Получать email-ы с объявлениями о написании статей." - diplomat_summary: "Существует большой интерес к CodeCombat в других странах, которые не говорят по-английски! Мы ищем переводчиков, которые готовы тратить свое время на перевод текстовой части сайта, так, чтобы CodeCombat стал доступен по всему миру как можно скорее. Если вы хотите помочь CodeCombat стать интернациональным, этот класс для вас." diplomat_introduction_pref: "Так, одной из вещей, которую мы узнали из " diplomat_launch_url: "запуска в октябре" diplomat_introduction_suf: "было то, что есть значительная заинтересованность в CodeCombat в других странах! Мы создаём корпус переводчиков, стремящихся превратить один набор слов в другой набор слов для максимальной доступности CodeCombat по всему миру. Если вы любите видеть контент до официального выхода и получать эти уровни для ваших соотечественников как можно скорее, этот класс для вас." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi diplomat_join_pref_github: "Найдите файл локализации вашего языка " diplomat_github_url: "на GitHub" diplomat_join_suf_github: ", отредактируйте его онлайн и отправьте запрос на подтверждение изменений. Кроме того, установите флажок ниже, чтобы быть в курсе новых разработок интернационализации!" - more_about_diplomat: "Узнать больше о том, как стать Дипломатом" diplomat_subscribe_desc: "Получать email-ы о i18n разработках и уровнях для перевода." - ambassador_summary: "Мы пытаемся создать сообщество, и каждое сообщество нуждается в службе поддержки, когда есть проблемы. У нас есть чаты, электронная почта и социальные сети, чтобы наши пользователи могли познакомиться с игрой. Если вы хотите помочь людям втянуться, получать удовольствие и учиться программированию, этот класс для вас." ambassador_introduction: "Это сообщество, которое мы создаём, и вы соединяете. У нас есть Olark чаты, электронная почта и социальные сети с уймой людей, с которыми нужно поговорить, помочь в ознакомлении с игрой и обучении из неё. Если вы хотите помочь людям втянуться, получать удовольствие, наслаждаться и и куда мы идём, этот класс для вас." ambassador_attribute_1: "Навыки общения. Уметь определять проблемы игроков и помогать решить их. Кроме того, держите всех нас в курсе о том, что игроки говорят, что им нравится, не нравится и чего хотят больше!" ambassador_join_desc: "расскажите нам немного о себе, чем вы занимались и чем хотели бы заниматься. Отсюда и начнём!" ambassador_join_note_strong: "Примечание" ambassador_join_note_desc: "Одним из наших главных приоритетов является создание мультиплеера, где игроки столкнутся с труднорешаемыми уровнями и могут призвать более высокоуровневых волшебников для помощи. Это будет отличным способом для послов делать свое дело. Мы будем держать вас в курсе!" - more_about_ambassador: "Узнать больше о том, как стать Послом" ambassador_subscribe_desc: "Получать email-ы о разработке мультиплеера и обновлениях в системе поддержки." changes_auto_save: "Изменения сохраняются автоматически при переключении флажков." diligent_scribes: "Наши старательные Писари:" @@ -722,10 +1026,10 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi rank_last_submitted: "отправлено " help_simulate: "Нужна помощь в симуляции игр?" code_being_simulated: "Ваш новый код участвует в симуляции других игроков для оценки. Обновление будет при поступлении новых матчей." - no_ranked_matches_pre: "Нет оценённых матчей для команды" + no_ranked_matches_pre: "Нет оценённых матчей для команды " no_ranked_matches_post: "! Сыграйте против нескольких противников и возвращайтесь сюда для оценки вашей игры." choose_opponent: "Выберите противника" - select_your_language: "Select your language!" + select_your_language: "Выберите ваш язык!" tutorial_play: "Пройти обучение" tutorial_recommended: "Рекомендуется, если вы раньше никогда не играли" tutorial_skip: "Пропустить обучение" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi fight: "В бой!" watch_victory: "Наблюдать за победой" defeat_the: "Победить" + tournament_started: ", начат" tournament_ends: "Турнир заканчивается" tournament_ended: "Турнир закончился" tournament_rules: "Правила турнира" tournament_blurb: "Пишите код, собирайте золото, стройте армию, крушите противников, получайте призы и улучшайте вашу карьеру в нашем \"$40,000 турнире жадности\"! Узнайте больше" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" + tournament_blurb_criss_cross: "Выигрывайте ставки, создавайте пути, перехитрите оппонентов, собирайте самоцветы и улучшайте вашу карьеру в нашем турнире Criss-Cross! Узнайте больше" + tournament_blurb_zero_sum: "Дайте волю своей программистской фантазии в собирании золота и боевой тактике в этом высокогорном зеркальном матче между красным волшебником и синим волшебником. Турнир начался в пятницу, 27 марта, и продолжится до 17.00 PDT понедельника, 6 апреля. Участвуйте для веселья и славы! Посмотрите детали" tournament_blurb_blog: "в нашем блоге" rules: "Правила" winners: "Победители" @@ -763,14 +1069,15 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi no_achievements: "Нет заработанных достижений." favorite_prefix: "Предпочитаемый язык " favorite_postfix: "." + not_member_of_clans: "Не является членом какого-либо клана." achievements: last_earned: "Последнее" amount_achieved: "Количество" achievement: "Достижение" category_contributor: "Помощь" -# category_ladder: "Ladder" -# category_level: "Level" + category_ladder: "Ладдер" + category_level: "Уровень" category_miscellaneous: "Помощь" category_levels: "Уровни" category_undefined: "Неопределено" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi account: recently_played: "Недавно сыграно" no_recent_games: "Нет сыгранных игр за последние две недели." + payments: "Платежи" + purchased: "Куплено" + subscription: "Подписка" + invoices: "Счета" + service_apple: "Apple" + service_web: "Web" + paid_on: "Оплачено" + service: "Сервис" + price: "Цена" + gems: "Самоцветы" + active: "Активна" + subscribed: "Подписана" + unsubscribed: "Остановлена" + active_until: "Активна до" + cost: "Стоимость" + next_payment: "Следующий платеж" + card: "Карта" + status_unsubscribed_active: "Вы не подписаны, и счет не будет выставлен, однако ваш аккаунт все еще активен." + status_unsubscribed: "Получите доступ к новым уровням, героям, предметам и бонусным самоцветам с подпиской CodeCombat!" + + account_invoices: + amount: "Количество в долларах США" + declined: "Ваша карта была отклонена" + invalid_amount: "Пожалуйста, введите количество долларов США." + not_logged_in: "Для доступа к счетам войдите или создайте аккаунт." + pay: "Оплатить счет" + purchasing: "Покупка..." + retrying: "Ошибка сервера, пробуем еще раз." + success: "Оплата прошла успешно. Спасибо!" loading_error: could_not_load: "Ошибка загрузки с сервера" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi leaderboard: "таблица лидеров" user_schema: "Пользовательская Schema" user_profile: "Пользовательский профиль" + patch: "Патч" patches: "Патчи" patched_model: "Исходный документ" model: "Модель" @@ -819,39 +1156,66 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi systems: "Системы" component: "Компонент" components: "Компоненты" -# thang: "Thang" -# thangs: "Thangs" + thang: "Предмет" + thangs: "Предметы" level_session: "Ваша сессия" opponent_session: "Сессия противника" article: "Статья" user_names: "Никнеймы" -# thang_names: "Thang Names" + thang_names: "Имена предмета" files: "Файлы" -# top_simulators: "Top Simulators" + top_simulators: "Лучшие симуляторы" source_document: "Исходный документ" document: "Документ" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" + sprite_sheet: "Лист спрайтов" + employers: "Работодатели" + candidates: "Кандидаты" + candidate_sessions: "Сессии кандидатов" user_remark: "Пользовательские поправки" -# user_remarks: "User Remarks" + user_remarks: "Пользовательские поправки" versions: "Версии" items: "Предметы" + hero: "Герой" heroes: "Герои" - wizard: "Волшебник" achievement: "Достижение" -# clas: "CLAs" -# play_counts: "Play Counts" + clas: "ЛСС" + play_counts: "Счетчики игр" feedback: "Отзыв" + payment_info: "Информация о платеже" + campaigns: "Кампании" + poll: "Опрос" + user_polls_record: "История опросов" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" delta: added: "Добавлено" modified: "Изменено" +# not_modified: "Not Modified" deleted: "Удалено" -# moved_index: "Moved Index" + moved_index: "Перемещен индекс" text_diff: "Разница" -# merge_conflict_with: "MERGE CONFLICT WITH" + merge_conflict_with: "КОНФЛИКТ СЛИЯНИЯ С" no_changes: "Нет изменений" guide: @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi legal: page_title: "Юридическая информация" - opensource_intro: "CodeCombat - бесплатный проект с полностью открытым исходным кодом." + opensource_intro: "CodeCombat - проект с полностью открытым исходным кодом." opensource_description_prefix: "Посмотрите " github_url: "наш GitHub" opensource_description_center: "и посодействуйте, если вам понравилось! CodeCombat построен на десятках проектов с открытым кодом, и мы любим их. Загляните в " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi practices_title: "Уважаемые лучшие практики" practices_description: "Это наши обещания тебе, игроку, менее юридическим языком." privacy_title: "Конфиденциальность" - privacy_description: "Мы не будем продавать какую-либо личную информацию. Мы намерены заработать деньги с помощью рекрутинга в конечном счёте, но будьте уверены, мы не будем распространять вашу личную информацию заинтересованным компаниям без вашего явного согласия." + privacy_description: "Мы не будем продавать ваши персональные данные." security_title: "Безопасность" security_description: "Мы стремимся сохранить вашу личную информацию в безопасности. Как проект с открытым исходным кодом, наш сайт открыт для всех в вопросах пересмотра и совершенствования систем безопасности." email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi email_settings_url: "ваши email настройки" email_description_suffix: "или через ссылки в email-ах, которые мы отправляем, вы можете изменить предпочтения и легко отписаться в любой момент." cost_title: "Стоимость" - cost_description: "В настоящее время, CodeCombat 100% бесплатен! Одной из наших главных целей является сохранить его таким, чтобы как можно больше людей могли играть, независимо от места в жизни. Если небо потемнеет, мы, возможно, введём подписки, возможно, только на некоторый контент, но нам не хотелось бы. Если повезёт, мы сможем поддерживать компанию, используя" - recruitment_title: "Рекрутинг" - recruitment_description_prefix: "Здесь, в CodeCombat, вы собираетесь стать могущественным волшебником не только в игре, но и в реальной жизни." - url_hire_programmers: "Никто не может нанять программистов достаточно быстро" - recruitment_description_suffix: "поэтому, как только вы улучшите свои навыки и будете согласны, мы начнём демонстрировать ваши лучшие программистские достижения тысячам работодателей, пускающих слюни на возможность нанять вас. Они платят нам немного, они платят вам" - recruitment_description_italic: "много" - recruitment_description_ending: "сайт остаётся бесплатным и все счастливы. Таков план." + cost_description: "CodeCombat бесплатен для игры на базовых уровнях, с подпиской за $9.99 USD/месяц вы получите доступ к дополнительным ветвям уровней и 3500 бонусным самоцветам в месяц. Вы можете отказаться в 1 клик, и мы предлагаем 100% гарантию возврата денег." copyrights_title: "Авторские права и лицензии" contributor_title: "Лицензионное соглашение соавторов" contributor_description_prefix: "Все вклады, как на сайте, так и на нашем репозитории GitHub, подпадают под наше" @@ -928,44 +1286,28 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi nutshell_description: "Любые ресурсы, которые мы предоставляем в Редакторе уровней можно свободно использовать как вам нравится для создания уровней. Но мы оставляем за собой право ограничивать распространение уровней самих по себе (которые создаются на codecombat.com), чтобы за них могла взиматься плата в будущем, если до этого дойдёт." canonical: "Английская версия этого документа является определяющей и канонической. Если есть какие-либо расхождения между переводами, документ на английском имеет приоритет." -# ladder_prizes: -# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - - wizard_settings: - title: "Настройки волшебника" - customize_avatar: "Изменить свой аватар" - active: "Активно" - color: "Цвет" - group: "Группа" - clothes: "Одежда" - trim: "Отделка" - cloud: "Облако" - team: "Команда" - spell: "Заклинание" - boots: "Обувь" - hue: "Оттенок" - saturation: "Насыщенность" - lightness: "Светлость" + ladder_prizes: + title: "Турнирные призы" # This section was for an old tournament and doesn't need new translations now. + blurb_1: "Призами будут награждены (согласно" + blurb_2: "турнирным правилам)" + blurb_3: "лучшие игроки команд \"Human\" и \"Ogres\"." + blurb_4: "Две команды - вдвое больше призов!" + blurb_5: "(Будет двое победителей, двое занявших второе место и т.д.)" + rank: "Ранг" + prizes: "Призы" + total_value: "Итоговое значение" + in_cash: "наличными" + custom_wizard: "Пользовательский волшебник CodeCombat" + custom_avatar: "Пользовательский аватар CodeCombat" + heap: "шесть месяцев доступа \"Startup\"" + credits: "кредиты" + one_month_coupon: "купон: выберите Rails или HTML" + one_month_discount: "скидка 30%: выберите Rails или HTML" + license: "лицензия" + oreilly: "электронная книга на ваш выбор" account_profile: - settings: "Настойки" # We are not actively recruiting right now, so there's no need to add new translations for this section. + settings: "Настройки" # We are not actively recruiting right now, so there's no need to add new translations for this section. edit_profile: "Редактировать профиль" done_editing: "Завершить редактирование" profile_for_prefix: "Профиль для " @@ -974,7 +1316,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi not_featured: "Не включает" looking_for: "Ищет:" last_updated: "Последнее обновление:" - contact: "Контакты" + contact: "Связаться" active: "Ищу предложения работы в настоящее время" inactive: "Не ищу предложений работы в настоящее время" complete: "готово" @@ -999,110 +1341,110 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi links_name_help: "На что вы ссылаетесь?" links_link_blurb: "URL ссылки" basics_header: "Обновить базовую информацию." -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" + basics_active: "Ищет предложения" + basics_active_help: "Вы хотите поискать предложения прямо сейчас?" + basics_job_title: "Предпочитаемая роль в работе" basics_job_title_help: "Какую роль вы хотите?" basics_city: "Город" - basics_city_help: "Город, в котором Вы живете или хотели ли бы работать." + basics_city_help: "Город, в котором вы живете или хотели ли бы работать." basics_country: "Страна" -# basics_country_help: "Country you want to work in (or live in now)." + basics_country_help: "Страна, в которой вы хотите работать (или живете сейчас)." basics_visa: "Статус работы в США" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" + basics_visa_help: "У вас есть разрешение на работу в США, или вам понадобится помощь в получении визы? (Если вы живете в Канаде или Австралии, отметьте \"есть разрешение\".)" + basics_looking_for: "Ищу" basics_looking_for_full_time: "Полная занятость" basics_looking_for_part_time: "Частичная занятость" basics_looking_for_remote: "Удаленная работа" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." + basics_looking_for_contracting: "Контракт" + basics_looking_for_internship: "Практика" + basics_looking_for_help: "Какого рода разработчиком вы хотете быть?" + name_header: "Заполните ваше имя" + name_anonymous: "Анонимный разработчик" + name_help: "Ваше имя для представления работодателям, например, 'Nick Winter'." + short_description_header: "Напишите пару строк о себе" + short_description_blurb: "Добавьте слоган, чтобы помочь работодателям быстро узнать больше о вас." + short_description: "Слоган" + short_description_help: "Кто вы и что вы ищите? 140 символов максимум." + skills_header: "Навыки" + skills_help: "Расположите актуальные навыки разработчика в порядке мастерства." + long_description_header: "Опишите вашу желаемую позицию" + long_description_blurb: "Расскажите работодателям, как вы круты, и какую роль вы хотите." + long_description: "Описание себя" + long_description_help: "Опишите себя потенциальным работодателям. Будьте кратки и по существу. Мы рекомендуем исходить из позиции наиболее интересного вам. 600 символов максимум." work_experience: "Опыт работы" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." + work_header: "Опишите ваш опыт работы" + work_years: "Годы опыта" + work_years_help: "Сколько лет профессионального опыта (с получением денег) разработки ПО вы имеете?" + work_blurb: "Напишите ваш рабочий опыт, начиная с последнего времени." + work_employer: "Работодатель" + work_employer_help: "Наименование вашего работодателя" work_role: "Должность" -# work_role_help: "What was your job title or role?" + work_role_help: "Какова была ваша должность или роль?" work_duration: "Продолжительность" -# work_duration_help: "When did you hold this gig?" + work_duration_help: "Как долго вы работали в этой должности?" work_description: "Описание" -# work_description_help: "What did you do there? (140 chars; optional)" + work_description_help: "Что вы делали? (140 символов; опционально)" education: "Образование" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." -# education_school: "School" -# education_school_help: "Name of your school." + education_header: "Укажите ваше образование" + education_blurb: "Список ваших учебных испытаний." + education_school: "Учебное заведение" + education_school_help: "Наименование учебного заведения." education_degree: "Степень" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" + education_degree_help: "Какова ваша степень и область исследования?" + education_duration: "Даты" + education_duration_help: "Когда?" education_description: "Описание" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" + education_description_help: "Выделите что-нибудь из этого учебного опыта. (140 символов; опционально)" our_notes: "Наши заметки" -# remarks: "Remarks" + remarks: "Замечания" projects: "Проекты" projects_header: "Добавьте 3 проекта" projects_header_2: "Проекты (3 самых лучших)" -# projects_blurb: "Highlight your projects to amaze employers." + projects_blurb: "Укажите ваши проекты, чтобы поразить работодателей." project_name: "Название проекта" -# project_name_help: "What was the project called?" + project_name_help: "Какое у проекта было название?" project_description: "Описание" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." + project_description_help: "Кратко опишите проект." + project_picture: "Картинка" + project_picture_help: "Загрузите картинку размером 230x115 пикселей или больше, демонстрирующую проект." project_link: "Ссылка" project_link_help: "Ссылка на проект." -# player_code: "Player Code" + player_code: "Код игрока" employers: -# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." -# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." -# hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. + deprecation_warning_title: "Извините, в данный момент CodeCombat не набирает новых сотрудников." + deprecation_warning: "В настоящее время мы фокусируемся на разработчиков начального уровня нежели опытных." + hire_developers_not_credentials: "Нанимаем разработчиков, не данные о них." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. get_started: "Начнем" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" + already_screened: "Мы уже технически просеяли всех наших кандидатов" + filter_further: ", но вы можете профильтровать далее:" filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" + filter_visa_yes: "Разрешение на работу в США" + filter_visa_no: "Нет разрешения на работу" + filter_education_top: "Высшее образование" + filter_education_other: "Другое" filter_role_web_developer: "Веб-разработчик" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" + filter_role_software_developer: "Разработчик ПО" + filter_role_mobile_developer: "Разработчик мобильных приложений" filter_experience: "Опыт" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." + filter_experience_senior: "Опытный (Senior)" + filter_experience_junior: "Начинающий (Junior)" + filter_experience_recent_grad: "Выпускник" + filter_experience_student: "Студент" + filter_results: "результаты" + start_hiring: "Начать наем." + reasons: "Три причины, почему вы должны искать работников через нас:" + everyone_looking: "Все здесь ищут их следующую возможность." + everyone_looking_blurb: "Забудьте о 20% рейтинге откликов через LinkedIn InMail. Каждый в нашем списке хочет найти свое следующее место работы и ответит на ваше приглашение на интервью." + weeding: "Расслабьтесь; мы отобрали их для вас.." + weeding_blurb: "Каждый игрок в списке был просмотрен на предмет технических способностей. Мы так же ведем телефонные переговоры по выбранным кандидатам и делаем пометки в их профилях, чтобы сэкономить ваше время." + pass_screen: "Они пройдут ваш технический отбор." + pass_screen_blurb: "Просмотрите код каждого кандидата перед принятием решения. Один работодатель заметил, что количество наших разработчиков, прошедших их технический отбор, в 5 раз превышало число набранных через Hacker News." + make_hiring_easier: "Сделайте мой наем сотрудников проще, пожалуйста." + what: "Что такое CodeCombat?" + what_blurb: "CodeCombat - это многопользовательская браузерная игра про программирование. Игроки пишут код, чтобы контролировать свои войска в битвах против других разработчиков. Наши игроки имеют опыт во всех основных технических областях." + cost: "Как много мы просим взамен?" + cost_blurb: "Мы просим 15% оплаты за первый год работы и предлагаем 100% гарантию возврата денег в течении 90 дней. Мы не взимаем платы за кандидатов, которые уже были активно проинтервьюированы в вашей компании." candidate_name: "Имя" candidate_location: "Местонахождение" candidate_looking_for: "Ищет" @@ -1110,27 +1452,27 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi candidate_top_skills: "Лучшие навыки" candidate_years_experience: "Лет опыта" candidate_last_updated: "Последнее обновление" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" + candidate_who: "Кто" + featured_developers: "Рекомендуемые разработчики" + other_developers: "Прочие разработчики" + inactive_developers: "Неактивные разработчики" admin: -# av_espionage: "Espionage" # Really not important to translate /admin controls. -# av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" -# av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" + av_espionage: "Разведка" # Really not important to translate /admin controls. + av_espionage_placeholder: "Почта или юзернейм" + av_usersearch: "Поиск пользователей" + av_usersearch_placeholder: "Почта, юзернейм, имя, что угодно." + av_usersearch_search: "Поиск" av_title: "Панель администрирования" av_entities_sub_title: "Сущности" av_entities_users_url: "Пользователи" av_entities_active_instances_url: "Активные экземпляры" av_entities_employer_list_url: "Список работодателей" -# av_entities_candidates_list_url: "Candidate List" -# av_entities_user_code_problems_list_url: "User Code Problems List" + av_entities_candidates_list_url: "Список кандидатов" + av_entities_user_code_problems_list_url: "Список проблем с пользовательским кодом" av_other_sub_title: "Другое" av_other_debug_base_url: "База (для отладки base.jade)" u_title: "Список пользователей" -# ucp_title: "User Code Problems" + ucp_title: "Проблемы с пользовательским кодом" lg_title: "Последние игры" clas: "ЛСС" diff --git a/app/locale/sk.coffee b/app/locale/sk.coffee index 69a11569f..61acbf4d9 100644 --- a/app/locale/sk.coffee +++ b/app/locale/sk.coffee @@ -3,25 +3,26 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", slogan: "Nauč sa programovať pomocou hry" no_ie: "CodeCombat nefunguje v prehliadači Internet Explorer 8 a jeho starších verziách. Ospravedlňujeme sa." # Warning that only shows up in IE8 and older no_mobile: "CodeCombat nebol navrhnutý pre mobilné zariadenia a nemusí na nich fungovať správne!" # Warning that shows up on mobile devices - play: "Hraj" # The big play button that just starts playing a level + play: "Hraj" # The big play button that opens up the campaign view. old_browser: "Ajaj, prehliadač je príliš starý. CodeCombat na ňom nepôjde. Je nám to ľúto!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Skúsiť sa to dá, ale asi to nepôjde." - campaign: "Ťaženie" + ipad_browser: "Zlé správy: CodeCombat nebeží na iPade v prehliadači. Dobré správy: naša iPad aplikácia čaká na schválenie od Apple." + campaign: "Kampaň" for_beginners: "Pre začiatočníkov" -# multiplayer: "Multiplayer" # Not currently shown on home page + multiplayer: "Viac hráčov" # Not currently shown on home page for_developers: "Pre vývojárov" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Alebo stiahni pre iPad" nav: play: "Hraj" # The top nav bar entry where players choose which levels to play -# community: "Community" + community: "Komunita" editor: "Editor" blog: "Blog" forum: "Fórum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + account: "Účet" + profile: "Profil" + stats: "Štatistiky" + code: "Kód" admin: "Spravuj" # Only shows up when you are an admin home: "Domov" contribute: "Prispej" @@ -29,7 +30,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", about: "O projekte" contact: "Kontakt" twitter_follow: "Sleduj na twitteri" -# teachers: "Teachers" + teachers: "Učitelia" modal: close: "Zatvor" @@ -39,407 +40,650 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", page_not_found: "Stránka nenájdená" diplomat_suggestion: -# title: "Help translate CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. -# sub_heading: "We need your language skills." - pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Slovak but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Slovak." - missing_translations: "Until we can translate everything into Slovak, you'll see English when Slovak isn't available." -# learn_more: "Learn more about being a Diplomat" -# subscribe_as_diplomat: "Subscribe as a Diplomat" + title: "Pomôžte preložiť CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. + sub_heading: "Potrebujeme tvoje jazykové zručnosti." +# pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in {English} but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into {English}." +# missing_translations: "Until we can translate everything into {English}, you'll see English when {English} isn't available." + learn_more: "Zisti viac, ako byť Diplomat" + subscribe_as_diplomat: "Prihlásiť sa ako Diplomat" play: play_as: "Hraj ako" # Ladder page - spectate: "Sleduj" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + spectate: "Sledovať" # Ladder page + players: "hráči" # Hover over a level on /play + hours_played: "odohratých hodín" # Hover over a level on /play + items: "Predmety" # Tooltip on item shop button from /play + unlock: "Odomknúť" # For purchasing items and heroes + confirm: "Potvrdiť" + owned: "Vlastníš" # For items you own + locked: "Zamknuté" + purchasable: "Môžeš zakúpiť" # For a hero you unlocked but haven't purchased + available: "Dostupné" + skills_granted: "Získané schopnosti" # Property documentation details + heroes: "Hrdinovia" # Tooltip on hero shop button from /play + achievements: "Úspechy" # Tooltip on achievement list button from /play + account: "Účet" # Tooltip on account button from /play + settings: "Nastavenia" # Tooltip on settings button from /play + poll: "Anketa" # Tooltip on poll button from /play + next: "Ďalší" # Go from choose hero to choose inventory before playing a level + change_hero: "Zmeniť hrdinu" # Go back from choose inventory to choose hero + choose_inventory: "Vyzbrojiť sa s predmetmi" + buy_gems: "Zakúpiť drahokamy" + subscription_required: "Vyžaduje sa predplatné" + anonymous: "Anonymný hráč" level_difficulty: "Obtiažnosť." - campaign_beginner: "Ťaženie pre začiatočníkov" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Vyber si úroveň" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Môže si vybrať ktorúkoľvek z úrovní alebo ich prediskutovať na " - adventurer_forum: "fóre pre dobrodruhov" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... v ktorom sa naučíš mágiu programovania." - campaign_dev: "Náhodné ťažšie úrovne" - campaign_dev_description: "... v ktorych sa naučíš používať rozhranie a čeliť väčším výzvam." + campaign_beginner: "Kampaň pre začiatočníkov" + awaiting_levels_adventurer_prefix: "Každy týždeň 5 nových levelov." # {change} + awaiting_levels_adventurer: "Prihlás sa ako Dobrodruh" + awaiting_levels_adventurer_suffix: "budeš ako prvý hrať nové levely." + adjust_volume: "Zmeniť hlasitosť" campaign_multiplayer: "Aréna pre viacerých hráčov" campaign_multiplayer_description: "... v ktorej si zmeriaš svoje programátorské sily proti ostatným hráčom." - campaign_player_created: "Hráčmi vytvorené úrovne" - campaign_player_created_description: "... v ktorých sa popasuješ s kreativitou svojich <a href=\"/contribute#artisan\">kúzelníckych súdruhov</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Postupuješ míľovými krokmi ! Povedz kamarátom, čo si sa už naučil pomocou CodeCombatu." + email_invalid: "Neplatná emailová adresa." + form_blurb: "Vlož nižšie ich emailové adresy a my sa s nimi spojíme !" + form_label: "Emailová adresa" + placeholder: "emailová adresa" + title: "Výborná práca, nováčik" login: sign_up: "Vytvor účet" log_in: "Prihlás sa" -# logging_in: "Logging In" + logging_in: "Prihlasujem" log_out: "Odhlás sa" - recover: "obnov" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Zabudnuté heslo" + authenticate_gplus: "Prihlásiť s G+" + load_profile: "Nahrať G+ Profil" + finishing: "Finišujem" + sign_in_with_facebook: "Prihlás sa s Facebookom" + sign_in_with_gplus: "Prihlás sa s G+" + signup_switch: "Chceš vytvoriť účet?" signup: - create_account_title: "Vytvor si účet, nech si uložíš progres" - description: "Je to zdarma. Len treba zadať zopár detailov." email_announcements: "Chcem dostávať správy na email." - coppa: "13+ alebo mimo USA" - coppa_why: "(Prečo?)" creating: "Vytvára sa účet..." sign_up: "Registruj sa" log_in: "prihlás sa pomocou hesla" -# social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." + social_signup: "Môžeš sa zaregistrovať aj cez Facebook alebo Google+:" + required: "Najskôr sa musíš prihlásiť." + login_switch: "Máš už účet ?" recover: recover_account_title: "Obnov účet" - send_password: "Zašli záchranné heslo" -# recovery_sent: "Recovery email sent." + send_password: "Zašli nové heslo" + recovery_sent: "Email s novým heslom odoslaný." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Primárny" + secondary: "Sekundárny" + armor: "Zbroj" + accessories: "Príslušenstvo" + misc: "Rôzne" + books: "Knihy" common: + back: "Späť" # When used as an action verb, like "Navigate backward" + continue: "Pokračovať" # When used as an action verb, like "Continue forward" loading: "Načítava sa..." saving: "Ukladá sa..." sending: "Odosiela sa..." -# send: "Send" + send: "Odošli" cancel: "Zruš" save: "Ulož" -# publish: "Publish" -# create: "Create" + publish: "Publikuj" + create: "Vytvoriť" manual: "Manuál" -# fork: "Fork" + fork: "Klonovať" play: "Hraj" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + retry: "Znova" + actions: "Príkazy" + info: "Info" + help: "Pomoc" + watch: "Sleduj" + unwatch: "Zruš sledovanie" + submit_patch: "Odoslať Opravu" + submit_changes: "Odoslať zmeny" +# save_changes: "Save Changes" general: -# and: "and" + and: "a" name: "Meno" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" + date: "Dátum" + body: "Telo" + version: "Verzia" + pending: "Vybavuje sa" + accepted: "Prijaté" + rejected: "Zamietnuté" + withdrawn: "Uzatvorené" + submitter: "Odosielateľ" + submitted: "Odoslané" + commit_msg: "Popis ukladania" + review: "Preskúmať" + version_history: "História verzií" + version_history_for: "História verzií pre: " + select_changes: "Vyber dve zmeny pre porovnanie." + undo_prefix: "Späť" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Dopredu" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Prehraj náhľad tohto levelu" + result: "Výsledok" + results: "Výsledky" + description: "Popis" or: "alebo" -# subject: "Subject" + subject: "Predmet" email: "Email" -# password: "Password" + password: "Heslo" message: "Správa" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + code: "Kód" + ladder: "Rebríček" + when: "Kedy" + opponent: "Súper" + rank: "Hodnosť" + score: "Skóre" + win: "Viťazstvo" + loss: "Prehra" + tie: "Remíza" + easy: "Ľahká" + medium: "Stredná" + hard: "Ťažká" + player: "Hráč" + player_level: "Úroveň" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Bojovník" + ranger: "Strelec" + wizard: "Mág" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "sekunda" + seconds: "sekúnd" + minute: "minúta" + minutes: "minúty" + hour: "hodina" + hours: "hodiny" + day: "deň" + days: "dní" + week: "týžden" + weeks: "týždňov" + month: "mesiac" + months: "mesaicov" + year: "rok" + years: "rokov" -# play_level: -# done: "Done" -# home: "Home" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" -# guide: "Guide" -# restart: "Restart" -# goals: "Goals" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" -# action_timeline: "Action Timeline" -# click_to_select: "Click on a unit to select it." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" -# reload_title: "Reload All Code?" -# reload_really: "Are you sure you want to reload this level back to the beginning?" -# reload_confirm: "Reload All" -# victory_title_prefix: "" -# victory_title_suffix: " Complete" -# victory_sign_up: "Sign Up to Save Progress" -# victory_sign_up_poke: "Want to save your code? Create a free account!" -# victory_rate_the_level: "Rate the level: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" -# victory_go_home: "Go Home" # Only in old-style levels. -# victory_review: "Tell us more!" # Only in old-style levels. -# victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" -# guide_title: "Guide" -# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. -# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. -# tome_other_units: "Other Units" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). -# tome_select_a_thang: "Select Someone for " -# tome_available_spells: "Available Spells" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." -# tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." -# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" + play_level: + done: "Hotovo" + home: "Domov" # Not used any more, will be removed soon. + level: "Úroveň" # Like "Level: Dungeons of Kithgard" + skip: "Preskočiť" + game_menu: "Menu hry" + guide: "Návod" + restart: "Reštart" + goals: "Ciele" + goal: "Cieľ" + running: "Beží..." + success: "Úspech!" + incomplete: "Nekompletné" + timed_out: "Čas uplynul" + failing: "Zlyhanie" + action_timeline: "Časová os" + click_to_select: "Vyberte kliknutím." + control_bar_multiplayer: "Viac hráčov" + control_bar_join_game: "Vstúpiť do hry" + reload: "Znova" + reload_title: "Načítať celý kód odznova?" + reload_really: "Naozaj chcete reštartovať túto úroveň od začiatkúu" + reload_confirm: "Obnoviť všetko" + victory: "Víťazstvo" + victory_title_prefix: "" + victory_title_suffix: " Kompletné" + victory_sign_up: "Přihlásit se pre uloženie progresu" + victory_sign_up_poke: "Chceš uložiť svoj kód? Vytvorte si účet zdarma!" + victory_rate_the_level: "Ohodnoťte túto úroveň: " # Only in old-style levels. + victory_return_to_ladder: "Rebríčky" + victory_play_continue: "Pokračovať" + victory_saving_progress: "Stav ukladania" + victory_go_home: "Návrat Domov" # Only in old-style levels. + victory_review: "Povedz nám viac!" # Only in old-style levels. + victory_hour_of_code_done: "Skončil si?" + victory_hour_of_code_done_yes: "Áno, pre dnešok som skončil™!" + victory_experience_gained: "Získaných XP" + victory_gems_gained: "Získaných kryštálov" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." + victory_become_a_viking: "Staň sa vikingom!" + guide_title: "Návod" + tome_minion_spells: "Vaše obľúbené kúzla" # Only in old-style levels. + tome_read_only_spells: "Kúzla iba na čítanie" # Only in old-style levels. + tome_other_units: "Ostatné jednotky" # Only in old-style levels. + tome_cast_button_run: "Spustiť" + tome_cast_button_running: "Prebieha" + tome_cast_button_ran: "Spustené" + tome_submit_button: "Odoslať" + tome_reload_method: "Znovu načítať pôvodný kód pre túto metódu" # Title text for individual method reload button. + tome_select_method: "Vybrať metódu" + tome_see_all_methods: "Vybrať všetky metódy, ktoré môžu byť upravené" # Title text for method list selector (shown when there are multiple programmable methods). + tome_select_a_thang: "Zvoľte niekoho pre " + tome_available_spells: "Dostupné kúzla" + tome_your_skills: "Tvoje schopnosti" + tome_help: "Pomoc" + tome_current_method: "Súčasná metóda" + hud_continue_short: "Pokračovať" + code_saved: "Kód uložený" + skip_tutorial: "Preskoč (esc)" + keyboard_shortcuts: "Klávesové skratky" + loading_ready: "Pripravené!" + loading_start: "Začať level" + problem_alert_title: "Oprav svoj kód" + problem_alert_help: "Pomoc" + time_current: "Teraz:" + time_total: "Max:" + time_goto: "Choď na:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "Skús znova" + infinite_loop_reset_level: "Reštartuj level" + infinite_loop_comment_out: "Zakomentovať môj kód" + tip_toggle_play: "Prepnite prehrávanie/pauzu pomocou Ctrl+P." + tip_scrub_shortcut: "Ctrl+[ a Ctrl+] pre pretočenie a rychlý presun." # {change} + tip_guide_exists: "Kliknite na sprievodcu v hernom menu (hore na stránke), pre užitočné informácie." + tip_open_source: "CodeCombat je 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat spustil svoju beta verziu v Októbri, 2013." + tip_think_solution: "Myslite na riešenie, nie na problém." + tip_theory_practice: "Teoreticky nie je žiaden rozdiel medzi teoriou a praxou. Ale v praxi ten rozdiel je. - Yogi Berra" + tip_error_free: "Sú dva spôsoby ako písať programy bez chýb; iba ten tretí funguje. - Alan Perlis" + tip_debugging_program: "Pokiaľ je ladenie proces, pri ktorom sa odstraňujú chyby, programovanie musí potom byť proces, ktorým sa chyby pridávajú. - Edsger W. Dijkstra" + tip_forums: "Choďte na naše fórum a povedzte nám, čo si myslíte!" + tip_baby_coders: "V budúcnosti budú i bábätká Arcikúzelníci." + tip_morale_improves: "Načítánie bude prebiehať, pokiaľ sa nezlepší morálka." + tip_all_species: "Veríme v rovnaké možnosti učenia sa programovať pre všetky druhy." + tip_reticulating: "Sieťovanie chrbtíc." + tip_harry: "Hej kúzelík, " + tip_great_responsibility: "S umením kódovať prichádza aj umenie odstraňovať chyby." + tip_munchkin: "Ak nebudeš jesť zeleninu, v spánku si po teba príde prežúvavec." + tip_binary: "Na svete je 10 druhov ľudí. Tí, ktorí poznajú binárnu sústavu, a tí, čo nie." + tip_commitment_yoda: "Programátor musí byť hlboko vážny a zaujatý. ~ Yoda" + tip_no_try: "Urob. Alebo neurob. Neskúšaj. - Yoda" + tip_patience: "Musíš byť trpezlivý, mladý Padawan. - Yoda" + tip_documented_bug: "Zdokumentovaná chyba nie je chyba, je to funkcia." + tip_impossible: "Vždy sa to zdá nemožným... až kým to neurobíš. - Nelson Mandela" + tip_talk_is_cheap: "Slová, slová. Radšej mi ukáž kód. - Linus Torvalds" + tip_first_language: "Najväčšia katastrofa, ktorá ťa môže postihnúť - zvládnutie prvého programovacieho jazyka. - Alan Kay" + tip_hardware_problem: "Koľko programátorov je treba na výmenu žiarovky ? Žiadneho. Je to problém hardwaru." + tip_hofstadters_law: "Hofstadterov zákon: Vždy to trvá dlhšie ako očakávame a to i vtedy, ak berieme do úvahy tento zákon." + tip_premature_optimization: "Predčasná optimalizácia je koreňom všetkého zla. - Donald Knuth" + tip_brute_force: "V prípade pochybností použite brute force. - Ken Thompson" + tip_extrapolation: "Sú iba dva druhy ľudí: tí,čo dokážu extrapolovať z nekompletných dát ..." + tip_superpower: "Kódovanie sa dá porovnať so superschopnosťami." + tip_control_destiny: "V skutočnom open source máte právo riadiť svoj vlastný osud. - Linus Torvalds" + tip_no_code: "Žiadny kód nie je rýchlejší než žiadny kód." + tip_code_never_lies: "Kód nikdy neklame, komentáre niekedy áno. — Ron Jeffries" + tip_reusable_software: "Predtým ako môže byť software znovupoužiteľný, musí byť najskôr použiteľný." + tip_optimization_operator: "Každý programovací jazyk má svoj optimalizačný operátor. Vo väčšine jazykov je to ‘//’" + tip_lines_of_code: "Meranie postupu v programovaní podľa počtu riadkov je ako merať postup stavby lietadla podľa jeho váhy. — Bill Gates" + tip_source_code: "Chcem zmeniť svet, ale nechcú mi dať jeho zdrojový kód." + tip_javascript_java: "Porovnávať Javu a JavaScript je ako porovnávať auto a lietajúci koberec. - Chris Heilmann" + tip_move_forward: "Ak nevieš lietať, bež, ak nemôžeš bežať, kráčaj, ak nemôžeš kráčať, choď po štyroch, ale nech už robíš čokoľvek, musíš sa hýbať vpred - Martin Luther King Jr." + tip_google: "Máš problém, ktorý nevieš vyriešiť ? Vygoogluj si ho !" + tip_adding_evil: "Pridanie špitky zla." + tip_hate_computers: "Ľudia, ktorí si myslia, že nenávidia počítače, v skutočnosti nenávidia mizerných programátorov. - Larry Niven" + tip_open_source_contribute: "Aj ty môžeš zlepšiť CodeCombat !" + tip_recurse: "Iterácia je ľudská, rekurzia božská.. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." -# game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" -# multiplayer_tab: "Multiplayer" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + game_menu: + inventory_tab: "Inventár" + save_load_tab: "Ulož/Nahraj" + options_tab: "Nastavenie" + guide_tab: "Návod" + guide_video_tutorial: "Video Návod" + guide_tips: "Tipy" + multiplayer_tab: "Multiplayer" + auth_tab: "Registruj sa" + inventory_caption: "Vystroj svojho hrdinu" + choose_hero_caption: "Zvoľ hrdinu,jazyk" + save_load_caption: "... a zobraz históriu" + options_caption: "Uprav nastavenia" + guide_caption: "Dokumenty a tipy" + multiplayer_caption: "Hraj s priateľmi!" + auth_caption: "Ulož svoj progres." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Rebríček" + view_other_solutions: "Ukážka iných riešení" # {change} + scores: "Skóre" + top_players: "Najlepší hráči podľa" + day: "Dnes" + week: "Tento týždeň" + all: "Celkovo" + time: "Time" + damage_taken: "obdržanej újmy" + damage_dealt: "spôsobenej újmy" + difficulty: "obtiažnosti" + gold_collected: "Nazbierané zlato" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "Použi veci" + equipped_item: "Použité" + required_purchase_title: "Požadované" + available_item: "K dispozícii" + restricted_title: "Zakázané" + should_equip: "(klikni 2x pre použitie)" + equipped: "(používa sa)" + locked: "(zamknuté)" + restricted: "(obmedzené v tejto úrovni)" + equip: "Použi" + unequip: "Odlož" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + buy_gems: + few_gems: "Pár drahokamov" + pile_gems: "Hromada drahokamov" + chest_gems: "Truhlica drahokamov" + purchasing: "Prebieha nákup..." + declined: "Vaša karta bola odmietnutá" + retrying: "Chyba servera, obnovovanie." + prompt_title: "Nedostatok drahokamov" + prompt_body: "Chcete získať viac?" + prompt_button: "Vstúpiť do obchodu" + recovered: "Obnovenie už zakúpených drahokamov prebehlo úspěšne. Aktualizujte stránku prosím." + price: "x3500 / mesiac" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + subscribe: + comparison_blurb: "Uč sa dôkladnejšie vďaka predplatnému !" + feature1: "60+ základných úrovní v štyroch svetoch" # {change} + feature2: "7 mocných <strong>new hrdinov</strong> s jedinečnými schopnosťami!" # {change} + feature3: "30+ bonusových úrovní" # {change} + feature4: "<strong>3500 bonusových diamantov</strong> každý mesiac !" + feature5: "Video tutoriály" + feature6: "Prémiová emailová podpora" +# feature7: "Private <strong>Clans</strong>" + free: "Zdarma" + month: "mesiac" + subscribe_title: "Predplatné" + unsubscribe: "Zrušiť predplatné" + confirm_unsubscribe: "Potvrdiť zrušenie predplatného" + never_mind: "Nevadí, stále ťa máme radi" + thank_you_months_prefix: "Ďakujeme za tvoju podporu v posledných" + thank_you_months_suffix: "mesiacoch." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + choose_hero: + choose_hero: "Vyber si svojho hrdinu" + programming_language: "Programovací jazyk" + programming_language_description: "Aký programovací jazyk chceš použiť ? " + default: "Predvolený" + experimental: "Experimentálny" + python_blurb: "Jednoduchý ale výkonný, skvelý pre začiatočníkov aj expertov" + javascript_blurb: "Jazyk webových stránok. (Nie je to Java)" + coffeescript_blurb: "Krajšia syntax pre JavaScript" + clojure_blurb: "Moderný Lisp" + lua_blurb: "Jazyk na skriptovanie hier." + io_blurb: "Jednoduchý ale nejasný." + status: "Stav" + hero_type: "Typ" + weapons: "Zbrane" + weapons_warrior: "Meče - Krátka vzdialenosť, Žiadna mágia" + weapons_ranger: "Kuše, Pušky - Dlhá vzdialenosť, Žiadna mágia" + weapons_wizard: "Prútiky, Žezlá - Dlhá vzdialenosť, Mágia" + attack: "Újma" # Can also translate as "Attack" + health: "Zdravie" + speed: "Rýchlosť" + regeneration: "Regenerácia" + range: "Vzdialenosť" # As in "attack or visual range" + blocks: "Blokuje" # As in "this shield blocks this much damage" + backstab: "Pichnutie do chrbta" # As in "this dagger does this much backstab damage" + skills: "Schopnosti" + attack_1: "Upravuje na" + attack_2: "hodnotu udávanej újmy zbraňou pre typ" + attack_3: "." + health_1: "Upravuje hodnotu zdravia na" + health_2: "hodnoty zaručenej brnením" + health_3: "." + speed_1: "Pohybuje sa rýchlosťou" + speed_2: "metrov za sekundu." + available_for_purchase: "Dostupné na zakúpenie" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Úroveň, v ktorej môžeš hrdinu odomknúť:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Iba niektorí hrdinovia môžu hrať túto úroveň." -# options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." -# editor_config: "Editor Config" -# editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" -# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." + skill_docs: + writable: "zapisovatelné" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "iba na čítanie" + action_name: "názov" + action_cooldown: "Zaberie" + action_specific_cooldown: "Cooldown" + action_damage: "Poškodenie" + action_range: "Vzdialenosť" + action_radius: "Dosah" + action_duration: "Trvanie" + example: "Príklad" + ex: "pr." # Abbreviation of "example" + current_value: "Aktuálna hodnota" + default_value: "Východzia hodnota" + parameters: "Parametre" + returns: "Vracia" + granted_by: "Poskytnutné od" -# about: -# why_codecombat: "Why CodeCombat?" -# why_paragraph_1: "If you want to learn to program, you don't need lessons. You need to write a lot of code and have a great time doing it." -# why_paragraph_2_prefix: "That's what programming is about. It's gotta be fun. Not fun like" -# why_paragraph_2_italic: "yay a badge" -# why_paragraph_2_center: "but fun like" -# why_paragraph_2_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" -# why_paragraph_2_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." -# why_paragraph_3: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." -# press_title: "Bloggers/Press" -# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" -# press_paragraph_1_link: "press packet" -# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + save_load: + granularity_saved_games: "Uložené" + granularity_change_history: "História" + + options: + general_options: "Všeobecné nastavenia" # Check out the Options tab in the Game Menu while playing a level + volume_label: "Halsitosť" + music_label: "Hudba" + music_description: "Vypnúť/zapnúť hudbu na pozadí." + editor_config: "Konfigurácia editora" + editor_config_title: "Konfigurácia editora" + editor_config_level_language_label: "Jazyk pre túto úroveň" + editor_config_level_language_description: "Zvol programovací jazyk pre túto úroveň." + editor_config_default_language_label: "Predvolený programovací jazyk" + editor_config_default_language_description: "Zvol programovací jazyk, v ktorom chceš kódovať nové úrovne." + editor_config_keybindings_label: "Klávesové skratky" + editor_config_keybindings_default: "Predvolené (Ace)" + editor_config_keybindings_description: "Pridáva skratky známe z bežných editorov." + editor_config_livecompletion_label: "Živé automatické dokončovanie" + editor_config_livecompletion_description: "Ukáže návrhy automatického dokončovania počas písania." + editor_config_invisibles_label: "Zobraziť neviditeľné znaky" + editor_config_invisibles_description: "Zobrazí inak neviditeľné znaky ako medzera a tabulátor." + editor_config_indentguides_label: "Zobraziť odsadenie" + editor_config_indentguides_description: "Ukáže vertikálne čiary odsadenia." + editor_config_behaviors_label: "Chytré správanie" + editor_config_behaviors_description: "Automaticky doplňuje hranaté a oblé zátvorky a úvodzovky." + + about: + why_codecombat: "Prečo CodeCombat?" + why_paragraph_1: "Ak sa chceš naučiť programovať, nepotrebuješ lekcie. To, čo potrebuješ je možnosť písať veľa kódu a baviť sa pri tom." + why_paragraph_2_prefix: "O tom je programovanie. Nemá to byť zábava typu" + why_paragraph_2_italic: "jéj, mám ďalší odznak" + why_paragraph_2_center: ",ale nadšenie ako" + why_paragraph_2_italic_caps: "HNEĎ MAMI, LEN DOKONČÍM TÚTO ÚROVEŇ !" + why_paragraph_2_suffix: "CodeCombat je skutočná hra pre viacej hráčov, od ktorej sa dá ťažko odtrhnúť." + why_paragraph_3: "Ak už sa máš byť závislý na nejakej hre, tak nech je to táto, pri ktorej sa staneš čarodejníkom technického veku." + press_title: "Blogeri/Tlač" + press_paragraph_1_prefix: "Chceš o nás písať ? Môžeš si stiahnúť a použiť všetky zdroje zahrnuté v našom" + press_paragraph_1_link: "tlačovom balíčku" + press_paragraph_1_suffix: ". Všetky logá a obrázky môžeš použiť bez toho, aby si nás priamo kontaktoval." + team: "Tím" + george_title: "Spoluzakladateľ" + george_blurb: "Podnikateľ" + scott_title: "Spoluzakladateľ" + scott_blurb: "Ten rozumný" + nick_title: "Spoluzakladateľ" + nick_blurb: "Motivačný Guru" + michael_title: "Programátor" + michael_blurb: "Systémový administrátor" + matt_title: "Programátor" # {change} + matt_blurb: "Bicyklista" + cat_title: "Najvyššia remeselníčka" + cat_blurb: "Ohýbačka vzduchu" + josh_title: "Dizajnér hier" + josh_blurb: "Podlaha je láva" + jose_title: "Hudba" + jose_blurb: "Vzlet" + retrostyle_title: "Ilustrácia" + retrostyle_blurb: "Retro hry" + + teachers: + title: "CodeCombat: Informácie pre učiteľov" + intro_1: "CodeCombat je online hra, ktorá učí programovať. Študenti píšu kód v skutočných programovacích jazykoch." + intro_2: "Nie sú nutné žiadne predchádzajúce skúsenosti !" + free_title: "Koľko to stojí ?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." + free_1: "CodeCombat Basic is ZDARMA ! K dispozícii je 70+ úrovní pokrývajúcich každý koncept." # {change} + free_2: "Mesačné predplatné poskytuje prístup k videonávodom a k úrovniam na precvičenie navyše." + teacher_subs_title: "Pre učiteľov je predplatné zdarma !" + teacher_subs_1: "Napíšte na" # {change} + teacher_subs_2: "pre zriadenie mesačného predplatného zdarma." # {change} +# teacher_subs_3: "to set up your subscription." + sub_includes_title: "Čo zahrnuje predplatné ?" + sub_includes_1: "Študenti s mesačným predplatným získajú ku 70+ základným úrovniam aj :" # {change} + sub_includes_2: "40+ tréningových úrovní" # {change} + sub_includes_3: "Video návody" + sub_includes_4: "Prémiovú emailovú podporu" + sub_includes_5: "7 nových hrdinov s jedinečnými schopnosťami" # {change} + sub_includes_6: "3500 bonusových diamantov každý mesiac" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." + who_for_title: "Pre koho je určený CodeComabt ?" + who_for_1: "CodeCombat odporúčame pre žiakov od 9 rokov. Nie sú nutné žiadne predchádzajúce skúsenosti s programovaním." + who_for_2: "CodeCombat sme navrhli tak, aby oslovil chlapcov aj dievčatá." + material_title: "Aký je objem učebnej látky ?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." + material_1: "Asi 8 hodín bezplatného obsahu a ďalších 14 hodín pre predplatiteľov. 5 nových úrovní každý týždeň." # {change} + concepts_title: "Aké pojmy sú pokryté ?" + how_much_title: "Koľko stojí mesačné predplatné ?" + how_much_1: "" + how_much_2: "Mesačné predplatné" + how_much_3: ", ktoré môže byť kedykoľvek zrušené, stojí 9.99$." + how_much_4: "Zľavy pre väčšie skupiny:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." + sys_requirements_title: "Systémové požiadavky" + sys_requirements_1: "Moderný webový prehliadač. Nové verzie prehliadačov Chrome, Firefox alebo Safari. Internet Explorer 9 alebo novší." + sys_requirements_2: "CodeCombat nie je zatiaľ podprovaný pre iPad." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Ulož novú verziu" new_major_version: "Nová primárna verzia" + submitting_patch: "Odosielanie opravy..." cla_prefix: "Ak chcete uložiť svoje zmeny, musíte najprv súhlasiť s našou" -# cla_url: "CLA" -# cla_suffix: "." + cla_url: "licencia" + cla_suffix: "." cla_agree: "SÚHLASÍM" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Kontaktujte nás" welcome: "Sme radi že nám píšete! Použite tento formulár pre odsolanie správy." - contribute_prefix: "Ak máte záujem prispieť do projektu, pozrite sa na " - contribute_page: "túto stránku" - contribute_suffix: "!" forum_prefix: "Pre všetky ostatné verejné záležitosti, prosím vyskúšajte " forum_page: "naše fórum" forum_suffix: "." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Poslať odozvu" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -448,46 +692,52 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", title: "Nastvenia účtu" not_logged_in: "Prihlás sa alebo si vytvor účet." autosave: "Zmeny sa uložia automaticky" - me_tab: "Ja" + me_tab: "O mne" picture_tab: "Obrázok" -# upload_picture: "Upload a picture" + delete_account_tab: "Zruš svoj účet" + wrong_email: "Nesprávny email" +# wrong_password: "Wrong Password" + upload_picture: "Nahraj obrázok" + delete_this_account: "Vymaž natrvalo tento účet" + god_mode: "Božský mód" password_tab: "Heslo" emails_tab: "E-maily" admin: "Spravovať" new_password: "Nové heslo" new_password_verify: "Overenie" -# email_subscriptions: "Email Subscriptions" -# email_subscriptions_none: "No Email Subscriptions." -# email_announcements: "Announcements" -# email_announcements_description: "Get emails on the latest news and developments at CodeCombat." -# email_notifications: "Notifications" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." -# contributor_emails: "Contributor Class Emails" -# contribute_prefix: "We're looking for people to join our party! Check out the " -# contribute_page: "contribute page" -# contribute_suffix: " to find out more." -# email_toggle: "Toggle All" + type_in_email: "Zadaj email na potvrdenie zrušenia účtu" # {change} +# type_in_password: "Also, type in your password." + email_subscriptions: "Odoberať emailom" + email_subscriptions_none: "Žiadne odoberanie emailom." + email_announcements: "Oznámenia" + email_announcements_description: "Chcem dostávať emaily o novinkách a vývoji programu CodeCombat." + email_notifications: "Oznámenia" + email_notifications_summary: "Ovládanie osobných, automatických emailových oznámení o tvojej aktivite v programe CodeCombat." + email_any_notes: "Všetky oznámenia" + email_any_notes_description: "Deaktivuj, ak nechceš dostávať žiadne oznamovacie emaily." + email_news: "Novinky" + email_recruit_notes: "Pracovné príležitosti" + email_recruit_notes_description: "Ak budeš hrať veľmi dobre, môže sa stať, že ti ponúkneme novú a lepšiu prácu." + contributor_emails: "Prispievateľské emaily" + contribute_prefix: "Nechceš sa k nám pripojiť ? Pozri si " + contribute_page: "stránku pre prispievateľov," + contribute_suffix: " dozvieš sa tam viac." + email_toggle: "Vyber/Zruš všetko" error_saving: "Chyba pri ukladaní" saved: "Zmeny uložené" password_mismatch: "Heslá nesedia." -# password_repeat: "Please repeat your password." + password_repeat: "Zopakuj, prosím, svoje heslo." # job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated # job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" - wizard_tab: "Kúzelník" - wizard_color: "Farba kúzelníckej róby" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,39 +753,88 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + community: + main_title: "CodeCombat Komunita" + introduction: "Pozri si spôsoby ako sa môžeš zapojiť a rozhodni sa, čo ťa najviac láka.Tešime sa na spoluprácu !" + level_editor_prefix: "Použi" + level_editor_suffix: "na vytvorenie a úpravu úrovní. Uživatelia vytvorili úrovne pre svoje triedy, svojích priateľov, žiakov, súrodencov a aj pre deň kódovania. Ak sa bojíš začínať celkom od začiatku, môžeš začať klonovaním a úpravou našich úrovní." + thang_editor_prefix: "Jednotky hry nazývame vecičky - 'thangs'. Použi" + thang_editor_suffix: "na úpravu grafiky. Povoľ jednotkám vrhať strely, zmeň smer animácie, zmeň body zásahu alebo nahraj vlastné vektorové sprity." + article_editor_prefix: "Vidíš chybu v našich dokumentoch ? Chceš pridať inštrukcie k vlastným výtvorom ? Pozri sa na" + article_editor_suffix: "a pomôž hráčom, aby získali, čo najviac z hrania na CodeCombat." + find_us: "Nájdeš nás na týchto stránkach" + social_blog: "Prečítaj si blog na Sette" + social_discource: "Pridaj sa k diskusii na fóre Discourse" + social_facebook: "Daj Like CodeCombatu na Facebooku" + social_twitter: "Sleduj CodeCombat na Twitteri" + social_gplus: "Pripoj sa ku CodeCombatu na Google+" + social_hipchat: "Chatujte s nami vo verejnej HipChat miestnosti" + contribute_to_the_project: "Prispej svojou prácou" -# classes: -# archmage_title: "Archmage" -# archmage_title_description: "(Coder)" -# artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" -# scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" -# diplomat_title_description: "(Translator)" -# ambassador_title: "Ambassador" -# ambassador_title_description: "(Support)" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + + classes: + archmage_title: "Arcimág" + archmage_title_description: "(Programátor)" + archmage_summary: "Ak si vývojár so záujmom o kódovanie vzdelávacíh hier, staň sa Arcimágom a pomôž nám s vývojom CodeCombatu !" + artisan_title: "Remeselník" + artisan_title_description: "(Tvorca úrovní)" + artisan_summary: "Tvor a zdieľaj úrovne, ktoré si môžu zahrať tvoji priatelia. Staň sa Remeselníkom a nauč sa umenie učiť iných programovať." + adventurer_title: "Dobrodruh" + adventurer_title_description: "(Testovač úrovní)" + adventurer_summary: "Dostaň sa k naším novým úrovniam (dokonca aj k tým pre predplatiteľov) zdarma a o týždeň skôr. Pomôž nám odhaliť chyby pred ich zverejnemím." + scribe_title: "Pisár" + scribe_title_description: "(Editor)" + scribe_summary: "Dobrý kód potrebuje dobrú dokumentáciu. Píš, edituj a vylepši dokumenty, ktoré čítajú milióny hráčov na celom svete." + diplomat_title: "Diplomat" + diplomat_title_description: "(Prekladateľ)" + diplomat_summary: "CodeCombat je preložený vďaka naším diplomatom do 45+ jazykov. Pomôž nám s prekladom." + ambassador_title: "Ambasador" + ambassador_title_description: "(Poradca)" + ambassador_summary: "Kroť použivateľov nášho fóra a nasmeruj na správnu cestu tých, čo sa pýtajú. Naši ambasádori reprezentujú CodeCombat na celom svete." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" -# article: -# edit_btn_preview: "Preview" -# edit_article_title: "Edit Article" + article: + edit_btn_preview: "Náhľad" + edit_article_title: "Upraviť článok" -# contribute: -# page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" +# polls: +# priority: "Priority" + + contribute: + page_title: "Prispenie" + intro_blurb: "CodeCombat je 100% open source! Stovky nadšených hráčov nám pomohli dostať hru na dnešnú úroveň. Pripoj sa k nám a napíš ďalšiu kapitolu CodeCombatu, ktorý učí svet kódovať!" + alert_account_message_intro: "Ahoj!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -695,96 +999,128 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # translating_diplomats: "Our Translating Diplomats:" # helpful_ambassadors: "Our Helpful Ambassadors:" -# ladder: -# please_login: "Please log in first before playing a ladder game." -# my_matches: "My Matches" -# simulate: "Simulate" -# simulation_explanation: "By simulating games you can get your game ranked faster!" -# simulate_games: "Simulate Games!" -# simulate_all: "RESET AND SIMULATE GAMES" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" -# battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " -# summary_wins: " Wins, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" -# rank_submitting: "Submitting..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" -# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." -# choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" -# tutorial_play: "Play Tutorial" -# tutorial_recommended: "Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" -# tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" -# warmup: "Warmup" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + ladder: + please_login: "Pred hraním rebríčkovej hry sa musíš najskôr prihlásiť." + my_matches: "Moje súboje" + simulate: "Simuluj" + simulation_explanation: "Simulovaním sa dostane hra rýchlejšie do rebríčka !!" + simulate_games: "Simuluj hry !" + simulate_all: "RESETUJ A SIMULUJ HRY" + games_simulated_by: "Tebou simulované hry:" + games_simulated_for: "Pre teba simulované hry:" + games_simulated: "Simulované hry" + games_played: "Odohrané hry" + ratio: "Pomer" + leaderboard: "Rebríček" + battle_as: "Hraj ako " + summary_your: "Tvoje " + summary_matches: "Súboje - počet výhier " + summary_wins: ", počet prehier " + summary_losses: " " + rank_no_code: "Žiadny nový kód na ocenenie" + rank_my_game: "Oceň moju hru !" + rank_submitting: "Odosielam..." + rank_submitted: "Odoslané na ocenenie" + rank_failed: "Chyba pri oceňovaní" + rank_being_ranked: "Hra je oceňovaná" + rank_last_submitted: "odoslané " + help_simulate: "Pomôžeš so simuláciou hier ?" + code_being_simulated: "Tvoj nový kód je simulovaný iným hráčom. Rebríček sa obnoví po nových súbojoch." + no_ranked_matches_pre: "Žiadne ocenené súboje pre " + no_ranked_matches_post: " tím ! Hraj proti súperom a potom sa sem vráť a uvidíš ocenenie svojej hry." + choose_opponent: "Vyber si súpera" + select_your_language: "Vyber si jazyk !" + tutorial_play: "Hraj tutoriál" + tutorial_recommended: "Odporúčané, pokiaľ si ešte nikdy nehral" + tutorial_skip: "Preskoč tutoriál" + tutorial_not_sure: "Nie si si istý, čo sa deje ?" + tutorial_play_first: "Hraj najskôr tutoriál." + simple_ai: "Jednoduchá umelá inteligencia" + warmup: "Na rozohratie" + friends_playing: "Hra proti priateľom" + log_in_for_friends: "Prihlás sa a hraj s priateľmi !" + social_connect_blurb: "Pripoj sa a hraj proti svojím priateľom !" + invite_friends_to_battle: "Pozvi priateľov a bojuj s nimi !" + fight: "Bojuj !" + watch_victory: "Pozri si svoju výhru" + defeat_the: "Poraz" + tournament_started: ", spustený" + tournament_ends: "Turnaj končí" + tournament_ended: "Turnaj skončil" + tournament_rules: "Pravidlá turnaja" + tournament_blurb: "Píš kód, zbieraj mince, stavaj armády, rozdrv nepriateľov, vyhraj ceny v hodnote 40,000$. Greed tournament! Pozri sa na detaily." + tournament_blurb_criss_cross: "Vyhraj ponuky, buduj cesty, preľsti súperov,zbieraj diamanty grab gems a vylepši svoju kariéru v našom Krížovkárskom turnaji ! Pozri sa na detaily" + tournament_blurb_zero_sum: "Odviaž svoju kódovaciu kreativitu pri zbieraní mincí a bojovej taktike v spravodlivom vysokohorskom súboji medzi medzi červenou a modrou čarodejkou. Turnaj začal v piatok 27. marca 2015 a skončil 6. apríla 2015. Súťaž pre zábavu a slávu ! Pozri sa na detaily" + tournament_blurb_blog: "v našom blogu." + rules: "Pravidlá" + winners: "Víťazi" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + user: + stats: "Stats" + singleplayer_title: "Levely pre jedného hráča" + multiplayer_title: "Levely pre viac hráčov" + achievements_title: "Úspechy" + last_played: "Naposledy hrané" + status: "Stav" + status_completed: "Dokončené" + status_unfinished: "Nedokončené" + no_singleplayer: "Doteraz neboli odohrané žiadne hry pre jedného hráča." + no_multiplayer: "Doteraz neboli odohrané žiadne hry pre viac hráčov." + no_achievements: "Zatiaľ nemáš žiadne Úspechy." + favorite_prefix: "Tvoj obľúbený jazyk je " + favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + achievements: + last_earned: "Naposledy získané" + amount_achieved: "Množstvo" + achievement: "Úspechy" + category_contributor: "Prispievateľ" + category_ladder: "Rebríček" + category_level: "Level" + category_miscellaneous: "Rôzne" + category_levels: "Levely" + category_undefined: "Nekategorizované" + current_xp_prefix: "" + current_xp_postfix: " spolu" + new_xp_prefix: "" + new_xp_postfix: " získaných" + left_xp_prefix: "" + left_xp_infix: " do ďalšieho levelu " + left_xp_postfix: "" # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Nastavenia kúzelníka" - customize_avatar: "Uprav svojho avatara" -# active: "Active" -# color: "Color" -# group: "Group" - clothes: "Róba" - trim: "Lem" - cloud: "Obláčik" -# team: "Team" - spell: "Kúzlo" - boots: "Čižmy" - hue: "Odtieň" - saturation: "Sýtosť" - lightness: "Jas" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/sl.coffee b/app/locale/sl.coffee index 578d8f577..b0cd16380 100644 --- a/app/locale/sl.coffee +++ b/app/locale/sl.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # slogan: "Learn to Code by Playing a Game" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level +# play: "Play" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -35,16 +36,16 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # close: "Close" # okay: "Okay" -# not_found: -# page_not_found: "Page not found" + not_found: + page_not_found: "Stran ne obstaja" diplomat_suggestion: -# title: "Help translate CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. -# sub_heading: "We need your language skills." + title: "Pomagajte prevesti CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. + sub_heading: "Potrebujemo vaše znanje jezikov." pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Slovene but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Slovene." missing_translations: "Until we can translate everything into Slovene, you'll see English when Slovene isn't available." -# learn_more: "Learn more about being a Diplomat" -# subscribe_as_diplomat: "Subscribe as a Diplomat" + learn_more: "Več o tem kako postati Diplomat" + subscribe_as_diplomat: "Naročite se kot Diplomat" # play: # play_as: "Play As" # Ladder page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." -# login: -# sign_up: "Create Account" +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" + + login: + sign_up: "Ustvari račun" # log_in: "Log In" # logging_in: "Logging In" # log_out: "Log Out" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -127,21 +126,28 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # books: "Books" common: - loading: "Loading..." -# saving: "Saving..." -# sending: "Sending..." -# send: "Send" -# cancel: "Cancel" -# save: "Save" -# publish: "Publish" -# create: "Create" -# manual: "Manual" -# fork: "Fork" -# play: "Play" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + back: "Nazaj" # When used as an action verb, like "Navigate backward" + continue: "Nadaljuj" # When used as an action verb, like "Continue forward" + loading: "Nalaganje ..." + saving: "Shranjevanje ..." + sending: "Pošiljanje ..." + send: "Pošlji" + cancel: "Prekliči" + save: "Shrani" + publish: "Objavi" + create: "Ustvari" + manual: "Priročnik" + fork: "Razveji" + play: "Poženi" # When used as an action verb, like "Play next level" + retry: "Ponovni poskus" + actions: "Dejanja" + info: "Info" + help: "Pomoč" + watch: "Opazuj" + unwatch: "Nehaj opazovati" + submit_patch: "Pošlji popravek" + submit_changes: "Pošlji spremembe" + save_changes: "Shrani spremembe" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/sr.coffee b/app/locale/sr.coffee index 5ff0ec565..5cfdc5e43 100644 --- a/app/locale/sr.coffee +++ b/app/locale/sr.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian slogan: "Научи да пишеш код играјући игру" no_ie: "CodeCombat не ради у IE8 и старијим верзијама. Жао нам је!" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat није дизајниран за мобилне уређаје и може да се деси да не ради!" # Warning that shows up on mobile devices - play: "Играј" # The big play button that just starts playing a level + play: "Играј" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" level_difficulty: "Тежина: " campaign_beginner: "Почетничка кампања" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Изабери ниво" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Можеш изабрати било који ниво испод, или разговарај о нивоима на " - adventurer_forum: "форуму Авантуриста" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... у којој учиш магију програмирања." - campaign_dev: "Насумични тежи нивои" - campaign_dev_description: "... у којима учиш о интерфејсу док радиш нешто теже." +# adjust_volume: "Adjust volume" campaign_multiplayer: "Арене за више играча" campaign_multiplayer_description: "... у којима кодираш 1 на 1 мечеве против осталих играча." - campaign_player_created: "Направљено од стране играча" - campaign_player_created_description: "... у којима се бориш против креативности својих колега." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "Направи Налог" log_in: "Улогуј Се" # logging_in: "Logging In" log_out: "Излогуј Се" - recover: "Поврати налог" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" signup: -# create_account_title: "Create Account to Save Progress" - description: "Бесплатно је. Још само пар ствари и готово:" email_announcements: "Примај обавештења на мејл" - coppa: "13+ или ван САД " - coppa_why: "(Зашто?)" creating: "Прављење налога..." sign_up: "Упиши се" log_in: "улогуј се са шифром" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" recover: recover_account_title: "Поврати налог" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Учитавање" saving: "Чување..." sending: "Шаље се..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # fork: "Fork" play: "Нивои" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian reload_title: "Поновно учитавање целог кода?" reload_really: "Да ли сте сигурни да желите да кренете ниво испочетка?" reload_confirm: "Поновно учитавање свега" +# victory: "Victory" # victory_title_prefix: "" victory_title_suffix: " Завршено" victory_sign_up: "Пријави се за новости" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian victory_rate_the_level: "Оцени ниво: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Играј следећи ниво" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" victory_go_home: "Иди на почетну" # Only in old-style levels. victory_review: "Реци нам више!" # Only in old-style levels. victory_hour_of_code_done: "Јеси ли завршио?" victory_hour_of_code_done_yes: "Да, завршио сам свој Сат Кода!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Водич" tome_minion_spells: "Чини твојих поданика" # Only in old-style levels. tome_read_only_spells: "Чини које се могу само гледати" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Изабери неког за " tome_available_spells: "Доступне чини" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Прилагоди Чаробњака" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" multiplayer_tab: "Мод за више играча" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Контактирај CodeCombat" welcome: "Драго нам је што нас контактираш! Искористи ову форму да нам пошаљеш мејл. " - contribute_prefix: "Ако си заинтересован да допринесеш пројекту, посети нашу " - contribute_page: "страницу за допринос" - contribute_suffix: "!" forum_prefix: "За било шта јавно, посети " forum_page: "наш форум." # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Пошаљи повратну информацију" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian autosave: "Измене се чувају аутоматски" me_tab: "Ја" picture_tab: "Фотографија" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Шифра" emails_tab: "Мејлови" # admin: "Admin" new_password: "Нова Шифра" new_password_verify: "Потврди" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "Мејл претплате" # email_subscriptions_none: "No Email Subscriptions." email_announcements: "Обавештења" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" - wizard_tab: "Чаробњак" - wizard_color: "Боја Одеће Чаробњака" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/sv.coffee b/app/locale/sv.coffee index 9304d9878..a88dbb9d3 100644 --- a/app/locale/sv.coffee +++ b/app/locale/sv.coffee @@ -2,15 +2,16 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr home: slogan: "Lär dig att koda genom att spela ett spel." no_ie: "CodeCombat fungerar tyvärr inte i IE8 eller äldre." # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat är inte designat för mobila enhter och fungerar kanske inte!" # Warning that shows up on mobile devices - play: "Spela" # The big play button that just starts playing a level + no_mobile: "CodeCombat är inte designat för mobila enheter och fungerar kanske inte!" # Warning that shows up on mobile devices + play: "Spela" # The big play button that opens up the campaign view. old_browser: "Oj då, din webbläsare är för gammal för att köra CodeCombat. Förlåt!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Du kan försöka ändå, men det kommer nog inte fungera." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." campaign: "Kampanj" for_beginners: "För nybörjare" multiplayer: "Flera spelare" # Not currently shown on home page for_developers: "För utvecklare" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Eller ladda ner till iPad" nav: play: "Spela" # The top nav bar entry where players choose which levels to play @@ -29,7 +30,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr about: "Om oss" contact: "Kontakt" twitter_follow: "Följ oss på Twitter" -# teachers: "Teachers" + teachers: "Lärare" modal: close: "Stäng" @@ -41,7 +42,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr diplomat_suggestion: title: "Hjälp till att översätta CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "Vi behöver dina språkliga kunskaper." - pitch_body: "Vi utvecklar CodeCombat på engelska, men vi har redan spelare världen över. Många av dem vill spela på svenska, men talar inte engelska, så om du talar båda språken, fundera på att registrera dig som Diplomat och hjälp till med översättningen av både hemsidan och alla nivår till svenska." + pitch_body: "Vi utvecklar CodeCombat på engelska, men vi har redan spelare världen över. Många av dem vill spela på svenska eftersom de inte talar engelska. Om du talar båda språken, fundera på att registrera dig som Diplomat och hjälp till med översättningen av både hemsidan och alla nivåer till svenska." missing_translations: "Tills vi har översatt allting till svenska, så kommer du se engelska när det inte finns någon svensk översättning tillgänglig." learn_more: "Läs mer om att vara en Diplomat" subscribe_as_diplomat: "Registrera dig som Diplomat" @@ -49,115 +50,133 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr play: play_as: "Spela som " # Ladder page spectate: "Titta på" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero + players: "spelare" # Hover over a level on /play + hours_played: "timmar spelade" # Hover over a level on /play + items: "Föremål" # Tooltip on item shop button from /play + unlock: "Lås upp" # For purchasing items and heroes + confirm: "Bekräfta" + owned: "Ägs av dig" # For items you own + locked: "Låst" + purchasable: "Till salu" # For a hero you unlocked but haven't purchased + available: "Tillgängligt" + skills_granted: "Ger färdigheter" # Property documentation details + heroes: "Hjältar" # Tooltip on hero shop button from /play + achievements: "Prestationer" # Tooltip on achievement list button from /play + account: "Konto" # Tooltip on account button from /play + settings: "Inställningar" # Tooltip on settings button from /play + poll: "Omröstning" # Tooltip on poll button from /play + next: "Nästa" # Go from choose hero to choose inventory before playing a level + change_hero: "Byt Hjälte" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + buy_gems: "Köp ädelstenar" + subscription_required: "Kräver prenumeration" + anonymous: "Anonym Spelare" level_difficulty: "Svårighetsgrad: " campaign_beginner: "Nybörjarkampanj" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Välj din nivå" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Du kan hoppa till vilken nivå som helst här under, eller diskutera nivåer på " - adventurer_forum: "Äventyrarforumet" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... i vilken du lär dig programmerandets magi." - campaign_dev: "Slumpad svårare nivå" - campaign_dev_description: "... där du lär dig att hantera gränssnittet medan du gör något lite svårare." + awaiting_levels_adventurer_prefix: "Vi släpper nya nivåer varje vecka." # {change} + awaiting_levels_adventurer: "Registrera dig som äventyrare" + awaiting_levels_adventurer_suffix: "för att vara först att spela nya nivåer." + adjust_volume: "justera volymen" campaign_multiplayer: "Flerspelararenor" campaign_multiplayer_description: "... i vilken du tävlar i kodande mot andra spelare" - campaign_player_created: "Spelarskapade" - campaign_player_created_description: "... i vilken du tävlar mot kreativiteten hos andra <a href=\"/contribute#artisan\">Hantverkare</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." + email_invalid: "ogiltig mejladress." + form_blurb: "Ange en förälders mejladress så visar vi dem!" + form_label: "Mejladress" + placeholder: "mejladress" +# title: "Excellent Work, Apprentice" login: sign_up: "Skapa konto" log_in: "Logga in" logging_in: "Loggar In" log_out: "Logga ut" - recover: "Glömt lösenord" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Glömt ditt lösenord?" + authenticate_gplus: "Autentisera G+" + load_profile: "Ladda G+ Profil" + finishing: "Färdigställer" + sign_in_with_facebook: "Logga in med Facebook" + sign_in_with_gplus: "Logga in med G+" + signup_switch: "Vill du skapa ett konto?" signup: - create_account_title: "Skapa ett konto för att spara dina framsteg" - description: "Det är gratis. Vi behöver bara lite information och sen är du redo att börja:" - email_announcements: "Mottag nyheter via e-post" - coppa: "13+ eller ej i USA" - coppa_why: " (Varför?)" + email_announcements: "Mottag nyheter via mejl" creating: "Skapar konto..." sign_up: "Skapa konto" log_in: "logga in med lösenord" social_signup: "Eller så kan du logga in genom facebook eller g+:" -# required: "You need to log in before you can go that way." + required: "Du måste logga in innan du kan gå dit" + login_switch: "Har du redan ett konto?" recover: recover_account_title: "Återskapa ditt konto" send_password: "Skicka återskapningslösenord" -# recovery_sent: "Recovery email sent." + recovery_sent: "Återskapningslösenord skickat." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Primär" + secondary: "Sekundär" + armor: "Rustning" + accessories: "Tillbehör" + misc: "Övrigt" + books: "Böcker" common: + back: "Tillbaka" # When used as an action verb, like "Navigate backward" + continue: "Fortsätt" # When used as an action verb, like "Continue forward" loading: "Laddar..." saving: "Sparar..." sending: "Skickar..." send: "Skicka" cancel: "Avbryt" save: "Spara" - publish: "Publisera" + publish: "Publicera" create: "Skapa" manual: "Manuellt" fork: "Förgrena" play: "Spela" # When used as an action verb, like "Play next level" -# retry: "Retry" + retry: "Försök igen" +# actions: "Actions" + info: "Info" + help: "Hjälp" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" + submit_changes: "Spara Ändringar" +# save_changes: "Save Changes" general: and: "och" name: "Namn" -# date: "Date" + date: "Datum" body: "Kropp" version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" commit_msg: "Förbindelsemeddelande" -# version_history: "Version History" - version_history_for: "Versionshistorik för: " +# review: "Review" + version_history: "Ändringshistorik" + version_history_for: "Ändringshistorik för: " + select_changes: "Välj två ändringar nedan för att se skillnaden." + undo_prefix: "Ångra" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "gör om" + redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" result: "Resultat" results: "Resultat" description: "Beskrivning" or: "eller" -# subject: "Subject" - email: "E-post" + subject: "Ämne" + email: "Mejl" password: "Lösenord" message: "Meddelande" code: "Kod" @@ -172,8 +191,11 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr easy: "Lätt" medium: "Medium" hard: "Svår" -# player: "Player" + player: "Spelare" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Krigare" +# ranger: "Ranger" + wizard: "Trollkarl" units: second: "sekund" @@ -194,129 +216,172 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr play_level: done: "Klar" home: "Hem" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "Nivå" # Like "Level: Dungeons of Kithgard" + skip: "Hoppa över" + game_menu: "Spelmeny" guide: "Guide" restart: "Börja om" goals: "Mål" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" + goal: "Mål" + running: "Kör..." + success: "Du lyckades!" + incomplete: "Ej färdig" + timed_out: "Slut på tid" + failing: "Ingen framgång" action_timeline: "Händelse-tidslinje" click_to_select: "Klicka på en enhet för att välja den." # control_bar_multiplayer: "Multiplayer" # control_bar_join_game: "Join Game" -# reload: "Reload" + reload: "Ladda om" reload_title: "Ladda om all kod?" reload_really: "Är du säker på att du vill ladda om nivån från början?" reload_confirm: "Ladda om allt" -# victory_title_prefix: "" + victory: "Seger" + victory_title_prefix: "" victory_title_suffix: " Genomförd" victory_sign_up: "Registrera dig för att få uppdateringar" - victory_sign_up_poke: "Vill du ha de senaste nyheterna via e-post? Skapa ett gratiskonto så håller vi dig informerad!" + victory_sign_up_poke: "Vill du ha de senaste nyheterna via mejl? Skapa ett gratiskonto så håller vi dig informerad!" victory_rate_the_level: "Betygsätt nivån: " # Only in old-style levels. victory_return_to_ladder: "Gå tillbaka till stegen" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Spela nästa nivå" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "Fortsätt" + victory_saving_progress: "Sparar framsteg" victory_go_home: "Gå hem" # Only in old-style levels. victory_review: "Berätta mer!" # Only in old-style levels. victory_hour_of_code_done: "Är du klar?" victory_hour_of_code_done_yes: "Ja, jag är klar med min Hour of Code!" + victory_experience_gained: "XP mottaget" + victory_gems_gained: "Vunna ädelstenar" + victory_new_item: "Nytt föremål" + victory_viking_code_school: "Jösses vilken svår nivå du just klarade! Om du inte redan är en mjukvaruutvecklare så borde du vara det. Du bev precis fast-tracked för antagning vid Viking Code School, där du kan ta dina kunskaper till en ny nivå och bli en professionell webbutvecklare på 14 veckor." + victory_become_a_viking: "Bli en Viking" guide_title: "Guide" tome_minion_spells: "Dina soldaters förmågor" # Only in old-style levels. tome_read_only_spells: "Skrivskyddade förmågor" # Only in old-style levels. tome_other_units: "Andra enheter" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_run: "Kör" + tome_cast_button_running: "Kör..." + tome_cast_button_ran: "Körde" + tome_submit_button: "Lämna in" + tome_reload_method: "Ladda om den ursprungliga koden för den här metoden" # Title text for individual method reload button. + tome_select_method: "Välj en metod" + tome_see_all_methods: "Se alla metoder som du kan redigera" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Välj någon för " tome_available_spells: "Tillgängliga förmågor" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_your_skills: "Dina färdigheter" + tome_help: "Hjälp" + tome_current_method: "Nuvarande metod" + hud_continue_short: "Fortsätt" + code_saved: "Kod sparad" skip_tutorial: "Hoppa över (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" + keyboard_shortcuts: "Kortkommandon" + loading_ready: "Redo!" + loading_start: "Starta Nivå" + problem_alert_title: "Fixa din kod" + problem_alert_help: "Hjälp" + time_current: "Nu:" + time_total: "Max:" + time_goto: "Gå till:" + non_user_code_problem_title: "Kunde inte ladda nivå" + infinite_loop_title: "Oändlig loop upptäckt" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "Försök igen" + infinite_loop_reset_level: "Återställ Nivå" # infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." + tip_toggle_play: "Spela/pausa med Ctrl+P." + tip_scrub_shortcut: "Ctrl+] och Ctrl+[ spolar framåt och bakåt." # {change} # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" + tip_open_source: "CodeCombat är 100% öppen källkod!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat startade sin beta i oktober 2013." + tip_think_solution: "Tänk på lösningen, inte problemet." + tip_theory_practice: "Teoretiskt sett så är det ingen skillnad mellan teori och praktik. Men i praktiken så är det. - Yogi Berra" + tip_error_free: "Det finns två sätt att skriva felfria program; endast det tredje fungerar. - Alan Perlis" # tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" # tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." + tip_baby_coders: "I framtiden är till och med bebisar ärkemagiker." # tip_morale_improves: "Loading will continue until morale improves." # tip_all_species: "We believe in equal opportunities to learn programming for all species." # tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " + tip_harry: "Du e' en trollkarl, " # tip_great_responsibility: "With great coding skill comes great debug responsibility." # tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." + tip_binary: "Det finns 10 sorters människor på jorden, de som försår binära tal och de som inte gör det." # tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" + tip_no_try: "Gör. Eller gör inte. Försök finns inte. - Yoda" + tip_patience: "Tålamod du måste ha, unge Padawan. - Yoda" + tip_documented_bug: "En dokumenterad bugg är inte en bugg - det är en funktion." + tip_impossible: "Allt verkar alltid omöjligt ända tills någon gör det. - Nelson Mandela" + tip_talk_is_cheap: "Det är billigt att prata. Visa mig koden. - Linus Torvalds" # tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." + tip_hardware_problem: "Fråga: Hur många programmerare krävs för att byta en glödlampa? Svar: Inga, det är ett hårdvaruproblem." # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Skräddarsy trollkarl" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" + tip_no_code: "Inge kod är snabbare än ingen kod." + tip_code_never_lies: "Kod ljuger aldrig, kommentarer gör det ibland. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" + tip_move_forward: "Vad du än gör, fortsätt framåt. - Martin Luther King Jr." + tip_google: "Ett problem du inte kan lösa? Googla det!" + tip_adding_evil: "Också en nypa ondska." + tip_hate_computers: "Det är det som är grejen med folk som tror att de hatar datorer. Det de egentligen hatar är dåliga programmerare. - Larry Niven" + tip_open_source_contribute: "Du kan få CodeCombat att bli ännu bättre!" + tip_recurse: "Iteration är mänskligt, rekursion är gudomligt. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" - multiplayer_tab: "Flerspelareläge" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + inventory_tab: "Utrustning" + save_load_tab: "Spara/Ladda" + options_tab: "Inställningar" + guide_tab: "Guide" + guide_video_tutorial: "Videogenomgång" + guide_tips: "Tips" + multiplayer_tab: "Flerspelarläge" + auth_tab: "Registrera dig" + inventory_caption: "Utrusta din hjälte" + choose_hero_caption: "Välj hjälte, språk" + save_load_caption: "... och visa historik" + options_caption: "Konfigurera inställningar" + guide_caption: "Dokument och tips" + multiplayer_caption: "Spela med vänner!" + auth_caption: "Spara dina framsteg." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Topplista" + view_other_solutions: "Visa topplistor" + scores: "Poäng" + top_players: "Toppspelare efter" + day: "Idag" + week: "Den här veckan" + all: "Genom tiderna" + time: "Tid" + damage_taken: "Mottagen skada" + damage_dealt: "Åsamkad skada" + difficulty: "Svårighetsgrad" + gold_collected: "Samlat guld" + + inventory: + choose_inventory: "Använd föremål" + equipped_item: "Används" + required_purchase_title: "Krävs" + available_item: "Tillgänglig" + restricted_title: "Begränsad" + should_equip: "(dubbeklicka för att använda)" + equipped: "(används)" + locked: "(låst)" + restricted: "(begränsad på den här nivån)" + equip: "Använd" + unequip: "Sluta använda" # buy_gems: # few_gems: "A few gems" @@ -325,64 +390,149 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + choose_hero: + choose_hero: "Välj hjälte" + programming_language: "Programspråk" + programming_language_description: "Vilket programspråk vill du använda?" + default: "Standard" + experimental: "Experimentell" + python_blurb: "Enkelt men ändå kraftfullt, perfekt för nybörjare och experter." + javascript_blurb: "Webbens språk. (Inte samma sak som Java.)" + coffeescript_blurb: "Trevligare JavaScript-syntax." + clojure_blurb: "Ett modernt Lisp." + lua_blurb: "Språk för spelskript." + io_blurb: "Enkelt men obskyrt." + status: "Status" + hero_type: "Typ" + weapons: "Vapen" + weapons_warrior: "Svärd - Kort räckvidd, ingen magi" + weapons_ranger: "Armborst, pistoler - Lång räckvidd, ingen magi" + weapons_wizard: "Trollspön, stavar - Lång räckvidd, magi" + attack: "Attack" # Can also translate as "Attack" + health: "Hälsa" + speed: "Hastighet" + regeneration: "Regeneration" + range: "Räckvidd" # As in "attack or visual range" + blocks: "Blockerar" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" + skills: "Färdigheter" + attack_1: "Gör" + attack_2: "av noterad vapenskada för" + attack_3: "." + health_1: "Får" + health_2: "av noterad rustningshälsa för" + health_3: "." + speed_1: "Rör sig" + speed_2: "meter per sekund." + available_for_purchase: "Tillgänlig att köpa" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Nivå som låser upp:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Det är bara vissa hjältar som kan spela den här nivån." -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + skill_docs: + writable: "skrivbar" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "endast läsning" + action_name: "namn" + action_cooldown: "Tar" + action_specific_cooldown: "Återhämtningstid" + action_damage: "Skada" + action_range: "Räckvidd" + action_radius: "Radie" + action_duration: "Löptid" + example: "Exampel" + ex: "ex" # Abbreviation of "example" + current_value: "Nuvarande värde" + default_value: "Standardvärde" + parameters: "Parametrar" + returns: "Returnerar" + granted_by: "Ges av" + + save_load: + granularity_saved_games: "Sparat" + granularity_change_history: "Historik" options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." + general_options: "Allmänna inställningar" # Check out the Options tab in the Game Menu while playing a level + volume_label: "Volym" + music_label: "Musik" + music_description: "Stäng av/sätt på bakgrundsmusik." editor_config: "Ställ in redigerare" editor_config_title: "Redigerarinställningar" -# editor_config_level_language_label: "Language for This Level" + editor_config_level_language_label: "Språk på den här nivån" # editor_config_level_language_description: "Define the programming language for this particular level." # editor_config_default_language_label: "Default Programming Language" # editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." @@ -392,7 +542,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # editor_config_livecompletion_label: "Live Autocompletion" # editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." editor_config_invisibles_label: "Visa osynliga" - editor_config_invisibles_description: "Visar osynliga tecken såsom mellanrum och nyradstecken." + editor_config_invisibles_description: "Visar osynliga tecken, till exempel mellanrum och nyradstecken." editor_config_indentguides_label: "Visa indenteringsguider" editor_config_indentguides_description: "Visar vertikala linjer för att kunna se indentering bättre." editor_config_behaviors_label: "Smart beteende" @@ -407,39 +557,133 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr why_paragraph_2_italic_caps: "NEJ MAMMA JAG MÅSTE BLI KLAR MED DEN HÄR NIVÅN" why_paragraph_2_suffix: "Därför är CodeCombat ett flerspelarspel, inte en spelifierad kurs. Vi slutar inte förrän du inte kan sluta - men den här gången är det en bra sak." why_paragraph_3: "Om du tänker bli beroende av något spel, bli beroende av det här och bli en av teknikålderns trollkarlar." -# press_title: "Bloggers/Press" -# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" -# press_paragraph_1_link: "press packet" -# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + press_title: "Bloggare/Press" + press_paragraph_1_prefix: "Vill du skriva om oss? Ladda gärna ner och använd resurserna som finns i vårt" + press_paragraph_1_link: "presspaket" + press_paragraph_1_suffix: ". Alla loggor och bilder kan användas utan att kontakta oss direkt." + team: "Team" + george_title: "Medgrundare" + george_blurb: "Businesser" + scott_title: "Medgrundare" + scott_blurb: "Reasonable One" + nick_title: "Medgrundare" + nick_blurb: "Motivation Guru" + michael_title: "Programmerare" + michael_blurb: "Sys Admin" + matt_title: "Medgrundare" + matt_blurb: "Cyklist" + cat_title: "Chefshantverkare" + cat_blurb: "Airbender" + josh_title: "Speldesigner" + josh_blurb: "Floor Is Lava" + jose_title: "Musik" + jose_blurb: "Taking Off" + retrostyle_title: "Illustration" + retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "CodeCombat: Info till lärare" + intro_1: "CodeCombat är ett onlinespel som lär ut programmering. Eleverna skriver kod i riktiga programspråk." + intro_2: "Ingen erfarenhet krävs!" + free_title: "Hur mycket kostar det?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." + free_1: "CodeCombat Basic är GRATIS! Det finns över 80 gratis nivåer som täcker alla koncept." # {change} + free_2: "En månadsprenumeration ger tillgång till videogenomgångar och fler övningsnivåer." + teacher_subs_title: "Lärare får gratis prenumerationer!" + teacher_subs_1: "Kontakta" # {change} + teacher_subs_2: "för att sätta upp en gratis månadsprenumeration." # {change} +# teacher_subs_3: "to set up your subscription." + sub_includes_title: "Vad ingår i prenumerationen?" + sub_includes_1: "Förutom de 80+ grundläggande nivåerna får elever med en månadsprenumeration tillgång till följande:" # {change} + sub_includes_2: "60+ övningsnivåer" # {change} + sub_includes_3: "Videogenomgångar" + sub_includes_4: "Premium mejlsupport" + sub_includes_5: "7 nya hjältar med unika färdigheter att bemästra" # {change} + sub_includes_6: "3500 bonusädelstenar varje månad" + sub_includes_7: "Privata klaner" + monitor_progress_title: "Hur kan jag se elevernas framsteg?" + monitor_progress_1: "Du kan skapa en" + monitor_progress_2: "till din klass." + monitor_progress_3: "För att lägga till en elev, skicka en inbjudan till din klan. Finns på den här sidan: " + monitor_progress_4: "" + monitor_progress_5: "Efter att de anslutit ser du en sammanfattning av elevens framsteg på din klans sida." + private_clans_1: "Privata klaner ger ökad integritet och detaljerad framstegsinformation för varje elev." + private_clans_2: "För att skapa en privat klan, använd kryssrutan 'Gör klanen privat' när du skapar en" + private_clans_3: "." + who_for_title: "Vem är CodeCombat för?" + who_for_1: "Vi rekommenderar CodeCombat till elever som är minst 9 år. Inga programmeringskunskaper krävs." + who_for_2: "Vi har skapat CodeCombat för att locka både pojkar och flickor." + material_title: "Hur mycket material finns det?" + material_china: "Ungefär 30 timmars speltid över 140+ prenumerantnivåer - än så länge - med nya nivåer varje vecka." # {change} + material_1: "Ungefär 10 timmar fritt innehåll och ytterligare 20 timmar prenumerantinnehåll med nya nivåer varje vecka." # {change} + concepts_title: "Vilka koncept täcks?" + how_much_title: "Hur mycket kostar en månadsprenumeration?" + how_much_1: "En" + how_much_2: "månadsprenumeration" + how_much_3: "kostar $9.99 och kan avbrytas när som helst." + how_much_4: "Dessutom ger vi rabatt till större grupper:" + how_much_5: "Vi gör engångsköp med avdrag och årliga prenumerationer för grupper, som en klass eller skola. Kontakta" + how_much_6: "för mer information." + more_info_title: "Var kan jag hitta mer information?" + more_info_1: "Vårt" + more_info_2: "lärarforum" + more_info_3: "är ett bra ställe att skapa kontakter med andra lärare som använder CodeCombat." + sys_requirements_title: "Systemkrav" + sys_requirements_1: "En modern webbläsare. Nyare versioner av Chrome, Firefox, eller Safari. Internet Explorer 9 eller senare." + sys_requirements_2: "CodeCombat stöds inte på iPad än." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Spara ny version" new_major_version: "Ny betydande version" +# submitting_patch: "Submitting Patch..." cla_prefix: "För att spara ändringar måste du först godkänna vår" -# cla_url: "CLA" -# cla_suffix: "." + cla_url: "CLA" + cla_suffix: "." cla_agree: "JAG GODKÄNNER" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Kontakta CodeCombat" - welcome: "Kul att höra från dig! Använd formuläret för att skicka e-post till oss. " - contribute_prefix: "Om du är intresserad av att bidra, koll in vår " - contribute_page: "bidragarsida" - contribute_suffix: "!" + welcome: "Kul att höra från dig! Använd formuläret för att skicka mejl till oss. " forum_prefix: "För någonting offentligt, var vänlig testa " forum_page: "vårt forum" forum_suffix: " istället." + faq_prefix: "Det finns också en" + faq: "FAQ" + subscribe_prefix: "Om du behöver hjälp med en nivå," + subscribe: "köp en CodeCombat prenumeration." + subscribe_suffix: "Sedan hjälper vi dig gärna med din kod." + subscriber_support: "Eftersom du är en CodeCombat-prenumerant kommer ditt mejl att bli prioriterat." + screenshot_included: "Inkluderar skärmbild." + where_reply: "Vart ska vi skicka svaret?" send: "Skicka Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,24 +694,31 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr autosave: "Ändringar sparas automatiskt" me_tab: "Jag" picture_tab: "Profilbild" -# upload_picture: "Upload a picture" + delete_account_tab: "Ta bort ditt konto" + wrong_email: "Fel mejladress" +# wrong_password: "Wrong Password" + upload_picture: "Ladda upp en bild" + delete_this_account: "Ta bort det här kontot för alltid" +# god_mode: "God Mode" password_tab: "Lösenord" - emails_tab: "E-postadresser" + emails_tab: "Mejladresser" admin: "Administratör" new_password: "Nytt lösenord" new_password_verify: "Verifiera" - email_subscriptions: "E-postsprenumerationer" -# email_subscriptions_none: "No Email Subscriptions." + type_in_email: "Skriv in din mejladress för att bekräfta borttagandet" # {change} +# type_in_password: "Also, type in your password." + email_subscriptions: "Mejlprenumerationer" + email_subscriptions_none: "Inga mejlprenumerationer." email_announcements: "Meddelanden" - email_announcements_description: "Få e-post med de senaste nyheterna och utvecklingen på CodeCombat." - email_notifications: "Påminnelser" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." - contributor_emails: "E-post för bidragare" + email_announcements_description: "Få mejl med de senaste nyheterna och utvecklingen på CodeCombat." + email_notifications: "Underrättelser" + email_notifications_summary: "Kontroller för personliga, automatiska mejlunderrättelser relaterade till din aktivitet på CodeCombat." + email_any_notes: "Alla underrättelser" + email_any_notes_description: "Stäng av för att hindra alla mejl om aktiviteter." + email_news: "Nyheter" + email_recruit_notes: "Jobbtillfällen" + email_recruit_notes_description: "Om du spelar riktigt bra så kanske vi kontaktar dig för att erbjuda ett (bättre) jobb." + contributor_emails: "Mejl för bidragare" contribute_prefix: "Vi söker mer folk som vill var med och hjälpa till! Kolla in " contribute_page: " bidragarsidan " contribute_suffix: " för att få veta mer." @@ -475,26 +726,25 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr error_saving: "Ett fel uppstod när ändringarna skulle sparas" saved: "Ändringar sparade" password_mismatch: "De angivna lösenorden stämmer inte överens." -# password_repeat: "Please repeat your password." + password_repeat: "Upprepa ditt lösenord." # job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated # job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - wizard_tab: "Trollkarl" - wizard_color: "Trollkarlens klädfärg" + view_profile: "Visa din profil" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." + keyboard_shortcuts: + keyboard_shortcuts: "Kortkommandon" + space: "Mellanslag" + enter: "Enter" +# press_enter: "press enter" + escape: "Escape" + shift: "Shift" + run_code: "Kör nuvarande kod." + run_real_time: "Kör i realtid." + continue_script: "Fortsätt förbi nuvarande skript." + skip_scripts: "Hoppa över alla skript som kan hoppas över." + toggle_playback: "Spela/Pausa." # scrub_playback: "Scrub back and forward through time." # single_scrub_playback: "Scrub back and forward through time by a single frame." # scrub_execution: "Scrub through current spell execution." @@ -502,59 +752,118 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # toggle_grid: "Toggle grid overlay." # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." + maximize_editor: "Maximera/minimera kodredigeraren." -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + community: + main_title: "CodeCombat Community" + introduction: "Kolla in på vilka sätt du kan vara delaktig och välj det som låter mest kul. Vi ser fram emot att jobba med dig!" + level_editor_prefix: "Använd" + level_editor_suffix: "till att skapa och ändra nivåer. Användare har skapat nivåer till sina klasser, vänner, hackathons, elever och syskon. Om det låter svårt att skapa en nivå så kan du använda en av våra som grund!" + thang_editor_prefix: "Vi kallar enheter i spelet 'thangs'. Använd" + thang_editor_suffix: "till att ändra CodeCombats grafik. Låt enheter kasta saker, ändra riktningen på animeringar, ändra en enhets livslängd eller ladda upp din egen vektorgrafik." + article_editor_prefix: "Sett ett misstag i något av våra dokument? Vill du göra instruktioner till dina egna skapelser? Ta en titt på" + article_editor_suffix: "och hjälp CodeCombatspelare att få så mycket ut från sin speltid som möjligt." + find_us: "Här hittar du oss" + social_blog: "Läs CodeCombatbloggen på Sett" + social_discource: "Gå med i diskussionerna i vårt forum" + social_facebook: "Gilla CodeCombat på Facebook" + social_twitter: "Följ CodeCombat på Twitter" + social_gplus: "Följ CodeCombat på Google+" + social_hipchat: "Chatta med oss in det publika CodeCombat HipChatrummet" + contribute_to_the_project: "Bidra till projektet" + +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" classes: - archmage_title: "Huvudmagiker" + archmage_title: "Ärkemagiker" archmage_title_description: "(Kodare)" + archmage_summary: "Om du är en utvecklare som är intresserad av att koda utbildningsspel, bli då en ärkemagiker och hjälp oss bygga CodeCombat!" artisan_title: "Hantverkare" artisan_title_description: "(Nivåbyggare)" + artisan_summary: "Bygg och dela nivåer till dig och dina vänner. Bli en hantverkare för att lära dig konsten att lära andra att koda." adventurer_title: "Äventyrare" adventurer_title_description: "(Nivåtestare)" + adventurer_summary: "Få nya nivåer (till och med prenumerantinnehåll) gratis en vecka innan alla andra och hjälp oss fixa buggar innan nivåerna blir publika." scribe_title: "Skriftlärd" scribe_title_description: "(Artikelredigerare)" + scribe_summary: "Bra kod behöver bra dokumentation. Skriv, ändra och förbättra dokumenten som läses av miljoner spelare över hela världen." diplomat_title: "Diplomat" diplomat_title_description: "(Översättare)" + diplomat_summary: "CodeCombat är översätts till över 45 språk av våra diplomater. Hjälp oss genom att göra översättningar." ambassador_title: "Ambassadör" ambassador_title_description: "(Support)" + ambassador_summary: "Tämj forumanvändarna och hjälp dem som har frågor. Ambassadörerna representerar CodeCombat ute i världen." editor: main_title: "CodeCombatredigerare" - article_title: "Artikelredigerare" - thang_title: "Enhetsredigerare" - level_title: "Nivåredigerare" + article_title: "Artikelredigeraren" + thang_title: "Enhetsredigeraren" + level_title: "Nivåredigeraren" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" revert: "Återställ" revert_models: "Återställ modeller" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" -# more: "More" + more: "Mer" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" level_some_options: "Några inställningar?" level_tab_thangs: "Enheter" level_tab_scripts: "Skript" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # level_tab_thangs_all: "All" level_tab_thangs_conditions: "Startvillkor" level_tab_thangs_add: "Lägg till enheter" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" level_settings_title: "Inställningar" level_component_tab_title: "Nuvarande komponenter" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" article_search_title: "Sök artiklar här" thang_search_title: "Sök enhetstyper här" level_search_title: "Sök nivåer här" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Förhandsgranska" edit_article_title: "Redigera artikel" + polls: + priority: "Prioritet" + contribute: - page_title: "Bidragande" - character_classes_title: "Karaktärklasser" - introduction_desc_intro: "Vi har store förhoppningar för CodeCombat." - introduction_desc_pref: "Vi vill vara stället dit alla sorters programmerare kommer för att lära och spela tillsammans, introducera andra till kodandets underbara värld, och visa upp de bästa delarna av gemenskapen. Vi kan inte och vi vill inte gör det ensamma; vad som gör projekt som GitHub, Stack Overflow och Linux fantastiska är människorna som använder och bygger dem. Av den anledningen " - introduction_desc_github_url: "CodeCombat is totally open source" - introduction_desc_suf: ", och vi siktar på att tillhandahålla så många sätt som möjligt för dig att delta och göra det här projektet till lika mycket ditt som vårt." - introduction_desc_ending: "Vi hoppas att du vill vara med!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy och Matt" + page_title: "Att bidra" + intro_blurb: "CodeCombat är 100% öppen källkod! Hundratals hängivna spelare har hjälpt till att skapa det spel du ser idag. Häng med oss och skriv nästa kapitel i CodeCombats uppdrag att lära världen att koda!" alert_account_message_intro: "Hej där!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "Intresserad av att jobba med spelgrafik, användargränssnittsdesign, databas- och serveroptimering, flerspelarnätverkadnde, fysik, ljud eller spelmotorprestation? Vill du hjälpa till att bygga ett spel för att hjälpa andra människor lära sig det du är bra på? Vi har mycket att göra och om du är en erfaren programmerare och vill utveckla för CodeCombat är denna klassen för dig. Vi skulle älska din hjälp med att bygga det bästa programmeringsspelet någonsin." + alert_account_message: "För att prenumerera på klassmejl behöver du vara inloggad först." archmage_introduction: "En av de bästa delarna med att bygga spel är att de syntetiserar så många olika saker. Grafik, ljud, realtidsnätverkande, socialt netvärkande och så klart många av de vanligare aspekterna av programmering, från databashantering och serveradministration på låg nivå till användargränssnitt och gränsnittsbyggande. Det finns mycket att göra, och om du är en erfaren programmerare som längtar efter att dyka ner i CodeCombats detaljer kan den här klassen vara för dig. Vi skulle älska din hjälp med att bygga det bästa programmeringsspelet någonsin." class_attributes: "Klassattribut" archmage_attribute_1_pref: "Kunskap om " @@ -626,15 +943,12 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr archmage_attribute_2: "Viss erfarenhet av programmering och personligt initiativ. Vi hjälper dig att bli orienterad, men kan inte lägga mycket tid på att träna dig." how_to_join: "Hur man går med" join_desc_1: "Alla kan hjälpa till! Kolla bara in vår " - join_desc_2: "för att komma igång, och kryssa i rutan nedanför för att markera att du är en modig huvudmagiker och få de senaste nyheterna via email. Vill du chatta om vad som ska göras eller hur du bli mer involverad?" + join_desc_2: "för att komma igång, och kryssa i rutan nedanför för att markera att du är en modig ärkemagiker och få de senaste nyheterna via email. Vill du chatta om vad som ska göras eller hur du bli mer involverad?" join_desc_3: ", eller hitta oss i vår " join_desc_4: "så tar vi det därifrån!" join_url_email: "Maila oss" join_url_hipchat: "offentliga HipChat-rum" - more_about_archmage: "Lär dig mer om att bli en huvudmagiker" archmage_subscribe_desc: "Få mail om nya kodmöjligheter och tillkännagivanden." - artisan_summary_pref: "Vill du designa nivåer och utvidga CodeCombats arsenal? Folk spelar igenom vårt innehåll snabbare än vi kan bygga! För tillfället är vår nivåredigerare ganska mager, så var uppmärksam. Att skapa nivåer kommer att vara lite utmanande och buggigt. Om du har visioner av kampanjer som sträcker sig från for-loopar till" - artisan_summary_suf: ", är den här klassen för dig." artisan_introduction_pref: "Vi måste bygga fler nivåer! Människor kräver mer innehåll, och vi kan bara bygga en viss mängd själva. Just nu är arbetsstation nivå ett; vår nivåredigerare är knappt användbar ens av dess skapare, så var uppmärksam. Om du har visioner av kampanjer som sträcker sig från for-loopar till" artisan_introduction_suf: ", är den här klassen kanske något för dig." artisan_attribute_1: "Någon erfarenhet av att bygga liknande innehåll vore bra, som till exempel Blizzards nivåredigerare. Det är dock inget krav!" @@ -645,51 +959,41 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr artisan_join_step2: "Skapa en ny nivå och utforska existerande nivåer." artisan_join_step3: "Hitta oss i vårt offentliga HipChat-rum för hjälp." artisan_join_step4: "Anslå dina nivåer på forumet för feedback." - more_about_artisan: "Lär dig mer om att bli en hantverkare" artisan_subscribe_desc: "Få mail om nivåredigeraruppdateringar och tillkännagivanden" - adventurer_summary: "Låt oss vara tydliga med din roll: du är tanken. Du kommer att ta stor skada. Vi behöver människor som kan testa splitternya nivåer och hjälpa till att identifiera hur man kan göra saker bättre. Smärtan kommer att vara enorm; att göra bra spel är en lång process och ingen gör rätt första gången. Om du kan härda ut och tål mycket stryk är det här klassen för dig." adventurer_introduction: "Låt oss vara tydliga med din roll: du är tanken. Du kommer att ta stor skada. Vi behöver människor som kan testa splitternya nivåer och hjälpa till att identifiera hur man kan göra saker bättre. Smärtan kommer att vara enorm; att göra bra spel är en lång process och ingen gör rätt första gången. Om du kan härda ut och tål mycket stryk är det här klassen för dig." adventurer_attribute_1: "En törst efter att lära sig. Du vill lära dig att koda och vi vill lära dig att koda. Du kommer förmodligen att vara den som lär ut mest i det här fallet, dock." adventurer_attribute_2: "Karismatisk. Var varsammen tydlig med vad som behöver förbättras, och erbjud förslag på hur förbättringar kan ske." adventurer_join_pref: "Antingen träffar (eller rekryterar!) du en hantverkare och jobbar med denna, eller så kryssar du i rutan nedanför för att få mail när det finns nya nivåer att testa. Vi kommer också att anslå nivåer som behöver granskas på nätverk som" adventurer_forum_url: "vårt forum" adventurer_join_suf: "så om du föredrar att bli notifierad på sådana sätt, bli medlem där!" - more_about_adventurer: "Lär dig mer om att bli en äventyrare" adventurer_subscribe_desc: "Få mail när det finns nya nivåer att testa." - scribe_summary_pref: "CodeCombat kommer inte bara att vara ett gäng nivåer. Det kommer också att vara en resurs för programmeringskunskap som spelar kan koppla in sig i. På det sättet kan varje hantverkare länka till en detaljerad artikel för spelarens uppbyggelse:dokumentation på ett sätt som liknar vad " - scribe_summary_suf: " har byggt. Om du tycker om att förklara programmeringskoncept är det här klassen för dig." scribe_introduction_pref: "CodeCombat kommer inte att vara bara ett gäng nivåer. Det kommer också att inkludera en resurs för kunskap, en wiki av programmeringskoncept som nivåer kan ansluta till. På det sättet slipper varje hantverkare förklara i detalj vad en jämförelseoperator är, utan kan bara länka sin nivå till artikeln som förklarar det och redan är skriven, till spelarens uppbyggelse. Någonting i stil med vad " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " har byggt. Om du tycker att det är kul att uttrycka programmeringskoncept i Markdown-form, är det här klassen för dig." scribe_attribute_1: "Förmåga med ord är i princip allt du behöver. Inte bara grammatik och stavning, utan förmåga att förmedla komplicerade idéer till andra." contact_us_url: "Kontakta oss" scribe_join_description: "Berätta lite om dig själv, din erfarenhet med programmering och vilka saker du skulle vilja skriva om. Vi går vidare därifrån!" - more_about_scribe: "Lär dig mer om att bli en skriftlärd" scribe_subscribe_desc: "Få mail om tillkännagivanden om artiklar." - diplomat_summary: "Det finns ett stort intresse för CodeCombat i länder som inte pratar engelska! Vi letar efter översättare som är villiga att tillbringa tid med att översätta huvuddelen av webbplatsens ord så att CodeCombat är tillgänglig över hela världen så snart som möjligt. Om du vill hjälpa till med att göra CodeCombat internationellt är det här klassen för dig." diplomat_introduction_pref: "Om vi lärde oss någonting från " diplomat_launch_url: "lanseringen i oktober" diplomat_introduction_suf: "är det att det finns ett stort intresse för CodeCombat i andra länder! Vi bygger en kår av översättare ivriga att förvandla en samling ord till en annan samling ord för att få CodeCombat så tillgänglig i världen som möjligt. Om du gillar att få tjuvkikar på kommande innehåll och att få dessa nivåer till de andra i ditt land så snart som möjligt är det här kanske klassen för dig." diplomat_attribute_1: "Flytande engelska och språket du vill översätta till. När man förmedlar komplicerade idéer är det viktigt att ha ett starkt grepp om båda!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." + diplomat_i18n_page_prefix: "Du kan börja översätta nivåer genom att gå till vår" + diplomat_i18n_page: "översättningssida" + diplomat_i18n_page_suffix: ", eller använda vårt gränssnitt och hemsida på GitHub." diplomat_join_pref_github: "Hitta ditt språks locale-fil " diplomat_github_url: "på GitHub" diplomat_join_suf_github: ", redigera den online, och skicka en ryckförfrågan. Kryssa också i rutan här nedanför för att hålla dig uppdaterad om nya internationaliseringsutvecklingar." - more_about_diplomat: "Lär dig mer om att bli en diplomat" diplomat_subscribe_desc: "Få mail om i18n-utvecklingar och nivåer att översätta." - ambassador_summary: "Vi försöker bygga en gemenskap, och varje gemenskap behöver ett supportteam när det blir problem. Vi har chatt, mail och sociala nätverk så att våra användare kan bekanta sig med spelet. Om du vill hjälpa folk att bli involverade, ha kul, och lära dig en del programmering är det här klassen för dig." ambassador_introduction: "Det är en gemenskap vi bygger, och du är anslutningarna. Vi har Olark-chatter, mail och sociala nätverk med många människor att prata med och hjälpa bekanta sig med spelet och lära sig från. Om du vill hjälpa människor att bli involverade och ha kul, och ha bra koll på CodeCombats puls och var vi är på väg, kanske det här är klassen för dig." ambassador_attribute_1: "Kommunikationsfärdigheter. Kunna identifiera problemen spelarna har och hjälpa till att lösa dem. Också att hålla resten av oss informerade om vad spelarna säger, vad de gillar och vad de inte gillar och vad de vill ha mer av!" ambassador_join_desc: "berätta om dig själv, vad du har gjort och vad du skulle vara intresserad av att göra. Vi tar det därifrån!" ambassador_join_note_strong: "Notera" ambassador_join_note_desc: "En av våra högsta prioriteringar är att bygga ett flerspelarläge där spelare som har problem med att lösa nivåer kan kalla på trollkarlar av en högre nivå för att hjälpa dem. Det kommer att vara ett jättebra sätt för ambassadörer att göra sin grej. Vi håller dig informerad!" - more_about_ambassador: "Lär dig mer om att bli en ambassadör" ambassador_subscribe_desc: "Få mail om supportuppdateringar och flerspelarutvecklingar" changes_auto_save: "Förändringar sparas automatiskt när du ändrar kryssrutor." diligent_scribes: "Våra flitiga skriftlärda:" - powerful_archmages: "Våra kraftfulla huvudmagiker:" + powerful_archmages: "Våra kraftfulla ärkemagiker:" creative_artisans: "Våra kreativa hantverkare:" brave_adventurers: "Våra modiga äventyrare:" translating_diplomats: "Våra översättande diplomater:" @@ -702,11 +1006,11 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr simulation_explanation: "Genom att simulera matcher kan du få dina matcher rankade fortare." simulate_games: "Simulera matcher!" simulate_all: "ÅTERSTÄLL OCH SIMULERA MATCHER" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" + games_simulated_by: "Spel simulerade av dig:" + games_simulated_for: "Spel simulerade åt dig:" + games_simulated: "Simulerade spel" + games_played: "Spelade spel" + ratio: "Förhållande" leaderboard: "Resultattavla" battle_as: "Kämpa som " summary_your: "Dina " @@ -720,12 +1024,12 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr rank_failed: "Kunde inte ranka" rank_being_ranked: "Matchen blir rankad" # rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" + help_simulate: "Hjälp till att simulera spel?" code_being_simulated: "Din nya kod håller på att bli simulerad av andra spelare för rankning. Detta kommer att uppdateras allt eftersom nya matcher kommer in." no_ranked_matches_pre: "Inga rankade matcher för " no_ranked_matches_post: " laget! Spela mot några motståndare och kom sedan tillbaka it för att få din match rankad." choose_opponent: "Välj en motståndare" -# select_your_language: "Select your language!" + select_your_language: "Välj språk!" tutorial_play: "Spela tutorial" tutorial_recommended: "Rekommenderas om du aldrig har spelat tidigare" tutorial_skip: "Hoppa över tutorial" @@ -740,40 +1044,43 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + rules: "Regler" + winners: "Vinnare" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " + user: + stats: "Stats" + singleplayer_title: "Enspelarnivåer" + multiplayer_title: "Flerspelarnivåer" + achievements_title: "Prestationer" + last_played: "Senast spelad" + status: "Status" + status_completed: "Avklarad" + status_unfinished: "Ej avklarad" + no_singleplayer: "Inga spel i enkelspelarläge än." + no_multiplayer: "Inga spel i flerspelarläge än." + no_achievements: "Inga prestationer än." + favorite_prefix: "Favoritspråk: " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" + achievements: + last_earned: "Senast förvärvad den" + amount_achieved: "Antal" + achievement: "Prestation" # category_contributor: "Contributor" # category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" + category_level: "Nivå" + category_miscellaneous: "Övrigt" + category_levels: "Nivåer" + category_undefined: "Okategoriserad" # current_xp_prefix: "" # current_xp_postfix: " in total" # new_xp_prefix: "" @@ -782,9 +1089,38 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # left_xp_infix: " until level " # left_xp_postfix: "" -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." + account: + recently_played: "Spelade nyligen" + no_recent_games: "Inga spel spelade de senaste två veckorna." + payments: "Betalningar" +# purchased: "Purchased" + subscription: "Prenumeration" + invoices: "Fakturor" +# service_apple: "Apple" + service_web: "Webb" + paid_on: "Betalat den" +# service: "Service" + price: "Pris" + gems: "Ädelstenar" + active: "Aktiv" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" + cost: "Kostnad" + next_payment: "Nästa betalning" + card: "Kort" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,21 +1175,48 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" -# delta: +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" + + delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" # merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" + no_changes: "Inga ändringar" # guide: # temp: "Temp" @@ -873,26 +1237,20 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr opensource_description_prefix: "Spana in " github_url: "vår GitHub" opensource_description_center: " och hjälp till om du vill! CodeCombat är byggt på dussintals projekt med öppen källkod, och vi älskar dem. Se " - archmage_wiki_url: "vår Huvudmagiker-wiki" + archmage_wiki_url: "vår Ärkemagiker-wiki" opensource_description_suffix: "för en lista över mjukvaran som gör detta spel möjligt." practices_title: "Respektfulla \"best practices\"" practices_description: "Dessa är våra löften till dig, spelaren, på lite mindre juristspråk." privacy_title: "Integritet" - privacy_description: "Vi kommer inte att sälja någon av din personliga information. Vi har för avsikt att tjäna pengar genom rekrytering så småningom, men var så säker på att vi inte kommer att distribuera din personliga information till intresserade företag utan ditt explicita samtycke." +# privacy_description: "We will not sell any of your personal information." security_title: "Säkerhet" security_description: "Vi strävar efter att hålla din personliga information säker. Eftersom vår källkod är öppen är vår det fritt fram för vem som helst att granska och förbättra våra säkerhetssystem." - email_title: "Email" + email_title: "Mejl" email_description_prefix: "Vi kommer inte att översvämma dig med spam. Genom " - email_settings_url: "dina email-inställningar" - email_description_suffix: "eller genom länkar i mailen vi skickar kan du ändra dina inställningar och lätt avprenumerera när som helst." + email_settings_url: "dina mejlinställningar" + email_description_suffix: "eller genom länkar i mejlen vi skickar kan du ändra dina inställningar och lätt avprenumerera när som helst." cost_title: "Kostnad" cost_description: "För närvarande är CodeCombat 100 % gratis! Ett av våra främsta mål är att behålla det så, så att så många som möjligt kan spela, oavsett plats i livet. Om himlen mörknar, kanske vi behöver ta betalt för prenumerationer eller något innehåll, men helst slipper vi det. Med lite tur lyckas vi hålla liv i företag med:" - recruitment_title: "Rekrytering" - recruitment_description_prefix: "Här på CodeCombat kommer du att bli en mäktig trollkarl - inte bara i spelet, utan också i verkliga livet." - url_hire_programmers: "Ingen kan anställa programmerare tillräckligt snabbt" - recruitment_description_suffix: "så när du har vässat dina kunskaper, och om du godkänner, kommer vi att demonstrera dina största kodbedrifter för de tusentals arbetsgivare som dreglar över chansen att anställa dig. De betalar oss lite, de betalar dig" - recruitment_description_italic: "mycket" - recruitment_description_ending: "sajten fortsätter vara gratis och alla är nöjda. Det är planen." copyrights_title: "Upphovsrätt och licenser" contributor_title: "Överenskommelse för bidragarlicens" contributor_description_prefix: "Alla bidrag, både på sajten och på vårt GitHub-repo, faller under vår" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Trollkarlsinställningar" - customize_avatar: "Skräddarsy din avatar" -# active: "Active" -# color: "Color" -# group: "Group" - clothes: "Kläder" - trim: "Dekorationer" - cloud: "Moln" -# team: "Team" - spell: "Trollformel" - boots: "Stövlar" - hue: "Nyans" - saturation: "Mättnad" - lightness: "Ljusstyrka" - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/th.coffee b/app/locale/th.coffee index 95b7eace7..0ffae4f3a 100644 --- a/app/locale/th.coffee +++ b/app/locale/th.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # slogan: "Learn to Code by Playing a Game" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices - play: "เล่น" # The big play button that just starts playing a level + play: "เล่น" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -28,8 +29,8 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra legal: "Legal" about: "เกี่ยวกับเรา" contact: "ติดต่อเรา" - twitter_follow: "Follow me!" -# teachers: "Teachers" + twitter_follow: "ติดตามพวกเรา!" + teachers: "สำหรับครู" modal: close: "ปิด" @@ -39,94 +40,94 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra page_not_found: "ขออภัย ไม่พบหน้าเว็บที่คุณต้องการ" diplomat_suggestion: -# title: "Help translate CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. + title: "ช่วยเหลือการแปล CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "พวกเราต้องการทักษะภาษาของคุณ" pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Thai but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Thai." missing_translations: "Until we can translate everything into Thai, you'll see English when Thai isn't available." # learn_more: "Learn more about being a Diplomat" # subscribe_as_diplomat: "Subscribe as a Diplomat" -# play: + play: # play_as: "Play As" # Ladder page # spectate: "Spectate" # Ladder page -# players: "players" # Hover over a level on /play + players: "ผู้เล่น" # Hover over a level on /play # hours_played: "hours played" # Hover over a level on /play # items: "Items" # Tooltip on item shop button from /play # unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" + confirm: "ยืนยัน" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level +# poll: "Poll" # Tooltip on poll button from /play + next: "ต่อไป" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" + buy_gems: "ซื้อ Gems" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "ลงทะเบียนใหม่" log_in: "ลงชื่อเข้าใช้" logging_in: "กำลังเข้าสู่ระบบ" log_out: "ลงชื่อออก" - recover: "กู้บัญชีการใช้งาน" + forgot_password: "ลืมรหัสผ่าน" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" + sign_in_with_facebook: "ลงชื่อเข้าใช้ด้วย Facebook" + sign_in_with_gplus: "ลงชื่อเข้าใช้ด้วย G+" +# signup_switch: "Want to create an account?" signup: - create_account_title: "สร้างบัญชีใหม่เพื่อบันทึกความก้าวหน้า" -# description: "It's free. Just need a couple things and you'll be good to go:" email_announcements: "รับข่าวสารผ่านทางอีเมลล์" -# coppa: "13+ or non-USA " - coppa_why: "(ทำไม?)" creating: "กำลังสร้างบัญชีใหม่..." sign_up: "สมัคร" log_in: "เข้าสู่ระบบด้วยรหัสผ่าน" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" # send_password: "Send Recovery Password" # recovery_sent: "Recovery email sent." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "ไอเทมหลัก" + secondary: "ไอเทมรอง" + armor: "ชุดเกราะ" + accessories: "เครื่องประดับ" + misc: "อื่นๆ" + books: "หนังสือ" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "รอสักครู่..." saving: "กำลังบันทึก..." # sending: "Sending..." @@ -139,41 +140,62 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # fork: "Fork" play: "เล่น" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" -# general: + general: # and: "and" -# name: "Name" -# date: "Date" + name: "ชื่อ" + date: "วันที่" # body: "Body" -# version: "Version" + version: "เวอร์ชั่น" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." + undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" -# or: "or" + or: "หรือ" # subject: "Subject" -# email: "Email" -# password: "Password" + email: "อีเมล" + password: "รหัสผ่าน" # message: "Message" # code: "Code" # ladder: "Ladder" # when: "When" # opponent: "Opponent" # rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" + score: "คะแนน" + win: "ชนะ" + loss: "แพ้" + tie: "เสมอ" + easy: "ง่าย" + medium: "ปานกลาง" + hard: "ยาก" # player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "เลเวล" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "นักรบ" +# ranger: "Ranger" + wizard: "พ่อมด" units: second: "วินาที" @@ -195,16 +217,16 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra done: "เสร็จสิ้น" home: "หน้าแรก" # Not used any more, will be removed soon. # level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + skip: "ข้าม" + game_menu: "เมนูเกม" guide: "คู่มือ" restart: "เริ่มเล่นใหม่" goals: "เป้าหมาย" # goal: "Goal" # running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" + success: "สำเร็จ!" + incomplete: "ไม่สมบูรณ์" + timed_out: "หมดเวลา" # failing: "Failing" # action_timeline: "Action Timeline" # click_to_select: "Click on a unit to select it." @@ -214,24 +236,24 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" + victory: "ชนะ" # victory_title_prefix: "" victory_title_suffix: "เสร็จสิ้น" victory_sign_up: "สมัครสมาชิกเพื่ออัพเดท" # victory_sign_up_poke: "Want to save your code? Create a free account!" # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "เล่นด่านถัดไป" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_play_continue: "เล่นต่อ" + victory_saving_progress: "บันทึก" victory_go_home: "ไปหน้าแรก" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. victory_hour_of_code_done: "เสร็จหรือยัง?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" + victory_new_item: "ไอเทมใหม่" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -239,13 +261,14 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # tome_cast_button_run: "Run" # tome_cast_button_running: "Running" # tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" + tome_submit_button: "ส่ง" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" -# time_current: "Now:" +# problem_alert_help: "Help" + time_current: "เวลาตอนนี้:" # time_total: "Max:" # time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "ลองใหม่" # infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" + infinite_loop_comment_out: "เปลี่ยนโค้ดทั้งหมดเป็นคอมเม้นท์" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -283,19 +313,39 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # tip_patience: "Patience you must have, young Padawan. - Yoda" # tip_documented_bug: "A documented bug is not a bug; it is a feature." # tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" + tip_talk_is_cheap: "อย่าเสียเวลาพูดเลย ให้ผมดูโมค้ดของคุณดีกว่า - Linus Torvalds" # tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" # tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" + tip_extrapolation: "ในโลกนี้มีคนอยู่สองประเภท คือคนที่เดาจากข้อมูลที่ไม่ครบถ้วนได้...." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" + tip_source_code: "เราอยากจะเปลี่ยนโลกนี้ แต่เขาไม่ให้ Source Code ผม" +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" + tip_strong_opponents: "ศัตรูที่แข็งแกร่งที่สุด ก็ยังมีจุดอ่อน - อุจิวะ อิทาจิ" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: # inventory_tab: "Inventory" -# save_load_tab: "Save/Load" + save_load_tab: "เซฟ/โหลด" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" multiplayer_tab: "ผู้เล่นหลายคน" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." + leaderboard: + leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" + day: "วันนี้" + week: "สัปดาห์นี้" + all: "ตลอดกาล" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" + difficulty: "ระดับความยาก" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -318,17 +383,91 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # equip: "Equip" # unequip: "Unequip" -# buy_gems: + buy_gems: # few_gems: "A few gems" # pile_gems: "Pile of gems" # chest_gems: "Chest of gems" # purchasing: "Purchasing..." # declined: "Your card was declined" -# retrying: "Server error, retrying." + retrying: "เซิร์ฟเวอร์ไม่ตอบสนอง กำลังลองอีกครั้ง" + prompt_title: "Gems ไม่พอ" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" -# choose_hero: + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" + parents: "สำหรับผู้ปกครอง" + parents_title: "ถึงผู้ปกครอง : ลูกของคนกำลังฝึกเขียนโปรแกรมอยู่ คุณจะช่วยให้เขาเขียนต่อไหม" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." + group_discounts_1st: "สมัคร 1 คน" + group_discounts_full: "ราคาเต็ม" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" + group_discounts_12th: "สมัคร 12 คนขึ้นไป" + group_discounts_40: "ลด 40%" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" + + choose_hero: # choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" + programming_language: "ภาษาโปรแกรม" # programming_language_description: "Which programming language do you want to use?" # default: "Default" # experimental: "Experimental" @@ -336,20 +475,33 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # javascript_blurb: "The language of the web. (Not the same as Java.)" # coffeescript_blurb: "Nicer JavaScript syntax." # clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." + lua_blurb: "ภาษาเขียนเกม" # io_blurb: "Simple but obscure." # status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" +# hero_type: "Type" + weapons: "อาวุธ" + weapons_warrior: "ดาบ - ระยะใกล้, ไม่ใช้เวทย์" + weapons_ranger: "หน้าไม้, ปืน - ระยะไกล, ไม่ใช้เวทย์" # weapons_wizard: "Wands, Staffs - Long Range, Magic" # attack: "Damage" # Can also translate as "Attack" # health: "Health" # speed: "Speed" # regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" + range: "ระยะ" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -373,13 +525,11 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # granularity_saved_games: "Saved" # granularity_change_history: "History" -# options: + options: # general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level # volume_label: "Volume" -# music_label: "Music" + music_label: "ดนตรี" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -398,8 +548,8 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # editor_config_behaviors_label: "Smart Behaviors" # editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." -# about: -# why_codecombat: "Why CodeCombat?" + about: + why_codecombat: "ทำไมต้องเล่น CodeCombat?" # why_paragraph_1: "If you want to learn to program, you don't need lessons. You need to write a lot of code and have a great time doing it." # why_paragraph_2_prefix: "That's what programming is about. It's gotta be fun. Not fun like" # why_paragraph_2_italic: "yay a badge" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" + george_title: "ผู้ร่วมก่อตั้ง" # george_blurb: "Businesser" -# scott_title: "Programmer" + scott_title: "ผู้ร่วมก่อตั้ง" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" + nick_title: "ผู้ร่วมก่อตั้ง" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" + matt_title: "ผู้ร่วมก่อตั้งผู้ร่วมก่อตั้ง" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra autosave: "บันทึกการเปลี่ยนแปลงอัตโนมัติ" # me_tab: "Me" picture_tab: "รูปภาพ" + delete_account_tab: "ลบบัญชีผู้ใช้" +# wrong_email: "Wrong Email" + wrong_password: "รหัสผ่านผิด" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "รหัสผ่าน" # emails_tab: "Emails" # admin: "Admin" new_password: "รหัสผ่านใหม่" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." email_announcements: "ประกาศ" @@ -475,20 +726,19 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra error_saving: "บันทึกผิดพลาด" saved: "เปลี่ยนรหัสผ่าน" password_mismatch: "รหัสผ่านไม่ถูกต้อง" -# password_repeat: "Please repeat your password." + password_repeat: "จงใส่รหัสผ่านอีกครั้ง" # job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated # job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" -# keyboard_shortcuts: + keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" + space: "Space" + enter: "Enter" + press_enter: "กด enter" + escape: "Esc" # shift: "Shift" # run_code: "Run current code." # run_real_time: "Run in real time." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" + clans: + clan: "แคลน" + clans: "แคลน" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" -# contribute: +# polls: +# priority: "Priority" + + contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" + alert_account_message_intro: "หวัดดี!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/tr.coffee b/app/locale/tr.coffee index 5c3f716b8..2d805c8fc 100644 --- a/app/locale/tr.coffee +++ b/app/locale/tr.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t slogan: "Oyun oynayarak kodlamayı öğrenin" no_ie: "CodeCombat maalesef Internet Explorer 8 veya daha eski sürümlerde çalışmaz." # Warning that only shows up in IE8 and older no_mobile: "CodeCombat mobil cihazlar için tasarlanmamıştır bu sebeple mobil cihazlarda çalışmayabilir." # Warning that shows up on mobile devices - play: "Oyna" # The big play button that just starts playing a level + play: "Oyna" # The big play button that opens up the campaign view. old_browser: "Olamaz, Tarayıcınız CodeCombat'ı çalıştırmak için çok eski. Üzgünüz!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Deneyebilirsiniz, ama muhtemelen oyun çalışmayacaktır." + ipad_browser: "Kötü haber: CodeCombat iPad üzerinde tarayıcıda çalışmıyor. İyi haber: İstemci Apple'ın onayını bekliyor." campaign: "Senaryo Kipi" for_beginners: "Yeni Başlayanlar için" multiplayer: "Çoklu-oyuncu Kipi" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t confirm: "Devam et" owned: "Sahipsin" # For items you own locked: "Kilitli" + purchasable: "Satın alınabilir" # For a hero you unlocked but haven't purchased available: "Açık" -# skills_granted: "Skills Granted" # Property documentation details + skills_granted: "Edinilen Yetenekler" # Property documentation details heroes: "Kahramanlar" # Tooltip on hero shop button from /play achievements: "Başarımlar" # Tooltip on achievement list button from /play account: "Hesap" # Tooltip on account button from /play settings: "Ayarlar" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play next: "İleri" # Go from choose hero to choose inventory before playing a level change_hero: "Kahramanı Değiştir" # Go back from choose inventory to choose hero choose_inventory: "Ögeleri Giy" - buy_gems: "Taş satın a" - older_campaigns: "Daha Eski Görevler" + buy_gems: "Değerli Taş Satın Al" + subscription_required: "Abonelik Gerekli" anonymous: "Anonim Oyuncu" level_difficulty: "Zorluk: " campaign_beginner: "Acemi Seferi" -# awaiting_levels_adventurer_prefix: "We release five levels per week." - awaiting_levels_adventurer: "Maceracı olmak için kayıt ol" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Seviye Seçimi" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Aşağıdaki seviyelerden birini doğrudan oynayabilirsiniz, veya seviye ile ilgili " - adventurer_forum: "Maceracı forumunda" - adventurer_suffix: " tartışabilirsiniz." - campaign_old_beginner: "Eski Yeni Başlayan Görevi" - campaign_old_beginner_description: "Programlama büyüsünü öğrenmek için..." - campaign_dev: "Rastgele Daha Zor Seviyeler" - campaign_dev_description: "Biraz daha zor işlerle uğraşırken arayüzü öğrenmek için..." + awaiting_levels_adventurer_prefix: "Haftada beş bölüm yayınlıyoruz." # {change} + awaiting_levels_adventurer: "Yeni oyunları ilk oynayan olmak için" + awaiting_levels_adventurer_suffix: "Maceracı olarak kayıt ol." + adjust_volume: "Sesi ayarla" campaign_multiplayer: "Çok Oyunculu Meydanlar" campaign_multiplayer_description: "Diğer oyuncularla kafa kafaya verip kodlamak için..." - campaign_player_created: "Oyuncuların Oluşturdukları" - campaign_player_created_description: "<a href=\"/contribute#artisan\">Zanaatkâr Büyücüler</a>in yaratıcılıklarına karşı mücadele etmek için..." - campaign_classic_algorithms: "Klasik Algoritmalar" - campaign_classic_algorithms_description: "... Bilgisayar Bilimleri'nde öğrendiğiniz en yaygın algoritmalar." - campaign_forest: "Orman Senaryosu" - campaign_dungeon: "Zindan Senaryosu" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Harika iş çıkarıyorsun! Birine CodeCombat ile ne kadar öğrendiğinden bahset." + email_invalid: "E-posta adresi geçersiz." + form_blurb: "E-posta adreslerini aşağıya gir ve onlara göstereceğiz!" + form_label: "E-posta Adresi" + placeholder: "e-posta adresi" + title: "Harika İş, Çaylak" login: sign_up: "Hesap Oluştur" log_in: "Giriş Yap" logging_in: "Giriş Yapılıyor" log_out: "Çıkış Yap" - recover: "hesabı kurtar." -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Parolanı mı unuttun?" + authenticate_gplus: "G+'ı Yetkilendir" + load_profile: "G+ Profilini Yükle" + finishing: "Tamamlanıyor" + sign_in_with_facebook: "Facebook ile Oturum Aç" + sign_in_with_gplus: "G+ ile Oturum Aç" + signup_switch: "Hesap oluşturmak istiyor musun?" signup: - create_account_title: "İlerlemenizi Kaydetmek için Hesap Oluşturun" - description: "Kayıt ücretsizdir. Aşağıdakileri doldurmanız yeterli:" email_announcements: "E-posta duyurularını almak istiyorum" - coppa: "13 yaşından üzerindeyim, veya ABD'de yaşamıyorum" - coppa_why: "(Bu nedir?)" creating: "Hesap oluşturuluyor..." sign_up: "Kaydol" log_in: "buradan giriş yapabilirsiniz." social_signup: "veya Facebook ya da G+ ile oturum açabilirsiniz:" required: "Buraya gidebilmeniz için oturum açmanız gerekli." + login_switch: "Zaten bir hesabın var mı?" recover: recover_account_title: "Hesabı Kurtar" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t books: "Kitaplar" common: + back: "Geri" # When used as an action verb, like "Navigate backward" + continue: "Devam et" # When used as an action verb, like "Continue forward" loading: "Yükleniyor..." saving: "Kaydediliyor..." sending: "Gönderiliyor..." @@ -137,11 +138,16 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t create: "Oluştur" manual: "El ile" fork: "Çatalla" - play: "Oyna" # When used as an action verb, like "Sonraki Seviyeyi Oyna" + play: "Oyna" # When used as an action verb, like "Play next level" retry: "Yeniden Dene" + actions: "Eylemler" + info: "Bilgi" + help: "Yardım" watch: "İzle" unwatch: "İzlemeyi Bırak" submit_patch: "Yama Gönder" + submit_changes: "Değişiklikleri Gönder" +# save_changes: "Save Changes" general: and: "ve" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t date: "Tarih" body: "Gövde" version: "Sürüm" + pending: "Bekliyor" + accepted: "Kabul Edildi" + rejected: "Reddedildi" + withdrawn: "İptal Edildi" + submitter: "Gönderen" + submitted: "Gönderilme" commit_msg: "Gönderme İletisi" + review: "Önizle" version_history: "Geçmiş" version_history_for: "Sürüm Geçmişi: " + select_changes: "Farkı görmek için aşağıdan iki fark seçin." + undo_prefix: "Geri Al" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Yenile" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Geçerli bölümün önizlemesini oyna" result: "Sonuç" results: "Sonuçlar" description: "Açıklama" @@ -173,7 +192,10 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t medium: "Normal" hard: "Zor" player: "Oyuncu" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Seviye" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Savaşçı" + ranger: "Korucu" + wizard: "Sihirbaz" units: second: "saniye" @@ -192,9 +214,9 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t years: "yıl" play_level: - done: "Tamamdır" + done: "Bitti" home: "Anasayfa" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" + level: "Bölüm" # Like "Level: Dungeons of Kithgard" skip: "Atla" game_menu: "Oyun Menüsü" guide: "Rehber" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t reload_title: "Tüm kod yeniden yüklensin mi?" reload_really: "Bu seviyeyi en baştan yüklemek istediğinizden emin misiniz?" reload_confirm: "Tümünü Yeniden Yükle" + victory: "Zafer" victory_title_prefix: "" victory_title_suffix: "Tamamlandı " victory_sign_up: "İlerlemeyi Kaydetmek için Kaydolun" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t victory_rate_the_level: "Seviyeyi oyla:" # Only in old-style levels. victory_return_to_ladder: "Merdivene Dön" victory_play_continue: "Devam Et" - victory_play_skip: "İleri Atla" - victory_play_next_level: "Sonraki Seviyeyi Oyna: " - victory_play_more_practice: "Daha Fazla Alıştırma" - victory_play_too_easy: "Çok Kolay" - victory_play_just_right: "Tam Kıvamında" - victory_play_too_hard: "Çok Zor" victory_saving_progress: "İlerlemeyi Kaydediliyor" victory_go_home: "Anasayfaya Git" # Only in old-style levels. victory_review: "Daha fazla söyleyin!" # Only in old-style levels. victory_hour_of_code_done: "Bitirdiniz mi?" victory_hour_of_code_done_yes: "Evet, Kod Saatimi (Hour of Code) bitirdim!" + victory_experience_gained: "Kazanılan XP" + victory_gems_gained: "Kazanılan Taş" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "Rehber" tome_minion_spells: "Minyonlarınızın Büyüleri" # Only in old-style levels. tome_read_only_spells: "Salt Okunur Büyüler" # Only in old-style levels. @@ -242,11 +264,12 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t tome_submit_button: "Gönder" tome_reload_method: "Bu yöntem için özgün kodu yeniden yükle" # Title text for individual method reload button. tome_select_method: "Bir Yöntem Seçin" - tome_see_all_methods: "Düzenleyebileceğiniz tüm yöntemleri görün" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "Düzenleyebileceğiniz tüm yöntemleri görün" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Birini seç..." tome_available_spells: "Kullanılabilir Büyüler" tome_your_skills: "Yetenekleriniz" -# tome_current_method: "Current Method" + tome_help: "Yardım" + tome_current_method: "Geçerli Metod" hud_continue_short: "Devam" code_saved: "Kod Kaydedildi" skip_tutorial: "Atla (ESC)" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t loading_ready: "Hazır!" loading_start: "Seviyeyi Başlat" problem_alert_title: "Kodunu Düzelt" + problem_alert_help: "Yardım" time_current: "Şimdi:" - time_total: "Max:" + time_total: "Azami:" time_goto: "Git:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "Yeniden Dene" infinite_loop_reset_level: "Bölümü Sıfırla" infinite_loop_comment_out: "Kodumu Yorum Yap" tip_toggle_play: "Ctrl+P ile oynat/beklet." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." tip_open_source: "CodeCombat %100 açık kaynaktır!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat ilk beta sürümünü Ekim 2013'te başlattı." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Sihirbazı Düzenle" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "Envanter" save_load_tab: "Kaydet/Yükle" options_tab: "Seçenekler" guide_tab: "Rehber" + guide_video_tutorial: "Video Öğreticisi" + guide_tips: "İpuçları" multiplayer_tab: "Çoklu-oyuncu" auth_tab: "Kaydol" inventory_caption: "Kahramanınızı donatın" @@ -306,15 +356,30 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t multiplayer_caption: "Arkadaşlarla oyna!" auth_caption: "İlerlemenizi kaydedin." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + inventory: choose_inventory: "Ögeleri Donan" equipped_item: "Giyilmiş" + required_purchase_title: "Gerekli" available_item: "Açık" -# restricted_title: "Restricted" + restricted_title: "Kısıtlı" should_equip: "(iki kere tıklayarak giy)" equipped: "(giyildi)" locked: "(kitli)" -# restricted: "(restricted in this level)" + restricted: "(bu bölümde kısıtlı)" equip: "Giy" unequip: "Çıkar" @@ -325,13 +390,87 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" choose_hero: choose_hero: "Kahramanınızı Seçin" programming_language: "Programlama Dili" programming_language_description: "Hangi programlama dilini kullanmak istiyorsunuz?" -# default: "Normal" -# experimental: "Experimental" + default: "Öntanımlı" + experimental: "Deneysel" python_blurb: "Basit ancak güçlü. Python mükemmel bir genel amaçlı dildir." javascript_blurb: "Web'in dili." coffeescript_blurb: "Daha iyi JavaScript sözdizimi." @@ -339,6 +478,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t lua_blurb: "Oyun betik dili." io_blurb: "Basit fakat anlaşılması güç." status: "Durum" +# hero_type: "Type" weapons: "Silahlar" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # regeneration: "Regeneration" range: "Menzil" # As in "attack or visual range" blocks: "Blok" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" skills: "Yetenekler" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t volume_label: "Ses" music_label: "Müzik" music_description: "Arkaplan müziğini aç/kapat." - autorun_label: "Otomatik çalıştır" - autorun_description: "Otomatik kod çalıştırmayı denetle." editor_config: "Düzenleyici Yapılandırması" editor_config_title: "Düzenleyici Yapılandırması" editor_config_level_language_label: "Bu Bölümün Dili" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # press_paragraph_1_link: "press packet" press_paragraph_1_suffix: ". Tüm logolar bizimle iletişime geçilmeden kullanılabilir." team: "Takım" - george_title: "CEO" + george_title: "CEO" # {change} # george_blurb: "Businesser" - scott_title: "Programcı" + scott_title: "Programcı" # {change} # scott_blurb: "Reasonable One" - nick_title: "Programcı" + nick_title: "Programcı" # {change} # nick_blurb: "Motivation Guru" michael_title: "Programcı" # michael_blurb: "Sys Admin" - matt_title: "Programcı" + matt_title: "Programcı" # {change} # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Yeni Sürümü Kaydet" new_major_version: "Yeni Önemli Sürüm" + submitting_patch: "Yama Gönderiliyor..." cla_prefix: "Değişiklikleri kaydetmek için ilk olarak" cla_url: "KLA'mızı" cla_suffix: "kabul etmelisiniz." cla_agree: "KABUL EDİYORUM" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "CodeCombat ile İletişim" welcome: "Sizi dinlemek ne hoş! Bu form ile bize e-posta gönderebilirsiniz." - contribute_prefix: "Katkıda bulunmak isterseniz " - contribute_page: "katkı sayfasını" - contribute_suffix: " ziyaret edebilirsiniz." forum_prefix: "Daha kamuya açık soru ve görüşleriniz için " forum_page: "forumumuzu" forum_suffix: " kullanabilirsiniz." + faq_prefix: "Ayrıca bir" + faq: "SSS'de mevcut." +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Gönder" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t autosave: "Değişiklikler Kendiliğinden Kaydedilir" me_tab: "Ben" picture_tab: "Resim" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" upload_picture: "Bir Resim Yükle" +# delete_this_account: "Delete this account permanently" + god_mode: "Tanrı Kipi" password_tab: "Şifre" emails_tab: "E-postalar" admin: "Yönetici" new_password: "Yeni Şifre" new_password_verify: "Teyit Et" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "E-posta Abonelikleri" email_subscriptions_none: "E-posta aboneliği yok." email_announcements: "Duyurular" @@ -481,16 +732,15 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." sample_profile: "Örnek bir profil gör" view_profile: "Kendi Profilinize Bakın" - wizard_tab: "Sihirbaz" - wizard_color: "Sihirbaz Kıyafeti Rengi" keyboard_shortcuts: keyboard_shortcuts: "Klavye Kısayolları" space: "Boşluk" enter: "Enter" +# press_enter: "press enter" escape: "Escape" shift: "ÜstKarakter" -# run_code: "Run current code." + run_code: "Geçerli kodu çalıştır." run_real_time: "Eşzamanlı çalış." # continue_script: "Continue past current script." # skip_scripts: "Skip past all skippable scripts." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." community: main_title: "CodeCombat Topluluğu" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "Büyük Büyücü" archmage_title_description: "(Kod Yazarı)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "Zanaatkar" artisan_title_description: "(Bölüm Yapıcı)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "Maceracı" adventurer_title_description: "(Bölüm Oynanabilirlik Testçisi)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "Katip" scribe_title_description: "(Makale Editörü)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "Diplomat" diplomat_title_description: "(Çevirmen)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "Büyükelçi" ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: main_title: "CodeCombat Düzenleyici" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t thang_title: "Nesne Düzenleyici" level_title: "Bölüm Düzenleyici" achievement_title: "Başarı Düzenleyici" +# poll_title: "Poll Editor" back: "Geri" revert: "Geri al" revert_models: "Önceki Modeller" pick_a_terrain: "Bir Arazi Seçin" - small: "Küçük" + dungeon: "Zindan" + indoor: "İç" + desert: "Çöl" grassy: "Çimli" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Küçük" + large: "Büyük" fork_title: "Yeni Sürüm Çatalla" fork_creating: "Çatal Oluşturuluyor..." generate_terrain: "Arazi Oluştur" more: "Daha Fazla" wiki: "Viki" live_chat: "Canlı Sohbet" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" level_some_options: "Bazı Seçenekler?" level_tab_thangs: "Nesneler" level_tab_scripts: "Betikler" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t level_tab_thangs_all: "Tüm" level_tab_thangs_conditions: "Başlama Şartları" level_tab_thangs_add: "Nesne Ekle" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" delete: "Sil" duplicate: "Kopyala" +# stop_duplicate: "Stop Duplicate" rotate: "Döndür" level_settings_title: "Ayarlar" level_component_tab_title: "Geçerli Bileşenler" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # new_level_title_login: "Log In to Create a New Level" new_achievement_title: "Yeni Bir Başarı Oluştur" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" level_search_title: "Seviye ara" achievement_search_title: "Başarı Ara" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "Önizleme" edit_article_title: "Makaleyi Düzenle" +# polls: +# priority: "Priority" + contribute: page_title: "Katkıda Bulunma" - character_classes_title: "Karakter Sınıfları" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " - introduction_desc_github_url: "CodeCombat tümüyle açık kaynaklıdır" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" alert_account_message_intro: "Merhaba!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # join_desc_4: "and we'll go from there!" join_url_email: "E-Posta ile Bize ulaşın" join_url_hipchat: "Herkese açık HipChat odası" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t account: recently_played: "En Son Oynananlar" no_recent_games: "Son iki hafta içerisinde hiç oyun oynanmadı." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "Yüklenemiyor" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t leaderboard: "Sıralama" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t practices_title: "Saygı Çerçevesinde En İyi Uygulamalar" practices_description: "Saygıdeğer oyuncu, bunlar size verdiğimiz sözlerimizdir. Daha kolay anlaşılması açısından özet haline indirgenmiştir." privacy_title: "Mahremiyet" - privacy_description: "Hiçbir kişisel bilginizi pazarlamayacağız. Asıl amacımız işe alımlar ile para kazanmaktır. Nihai olarak, sizin onayınız olmadan, veriniz ile ilgilenen hiçbir şirket ile bilgi satışına dair pazarlık yapmayacağımıza dair sizi temin ederiz" +# privacy_description: "We will not sell any of your personal information." security_title: "Güvenlik" security_description: "Kişisel bilgilerinizi güvende tutmak için mücadele ediyoruz. Açık kaynaklı bir proje olarak, sitemiz herkesin görüşüne açıktır ve güvenlik sistemimizin geliştirilmesine yardımcı olabilirsiniz." email_title: "Eposta" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t email_description_suffix: "ister size gönderdiğimiz epostadaki linklerden, tercihlerinizi değiştirebilir ve aboneliğinizi anında iptal edebilirsiniz." cost_title: "Ücret" cost_description: "Şu anda CodeCombat tamamıyla ücretsiz! Esas amaçlarımızdan biri bu şekilde devam etmek, bu sayede hayattaki konumu fark etmeksizin olabildiğince çok insan oynayarak kodlamayı öğrenebilir. Eğer koşullar olumsuz yönde değişirse, abonelik veya bazı içerikler için belirli ücretler talep edilebilir, ama bunu tercih etmeyiz. Temennimiz şudur ki, şirketi şu biçimde sürdürmeye devam edebiliriz:" - recruitment_title: "İşe Alım" - recruitment_description_prefix: "CodeCombat'te, kudretli bir büyücü haline geleceksiniz–sadece oyunda değil, gerçek hayatta da." - url_hire_programmers: "Kimse yeterince hızlı bir şekilde programcı işe alamaz," - recruitment_description_suffix: "bu sebeple, becerilerinizi yeterince geliştirdiğinizde ve siz de kabul ettiğiniz takdirde, becerilerinize dair kısa bir tanıtımı, sizi işe almak için can atan kişilere göndereceğiz. Bize biraz, size" - recruitment_description_italic: "bayağı ödeyecekler" - recruitment_description_ending: "böylece site ücretsiz kalacak ve herkes memnun olacak. Plan bu." copyrights_title: "Telif Hakları ve Lisanslar" contributor_title: "Katılımcı Lisans Sözleşmesi" contributor_description_prefix: "GitHub ve siteye yapılan tüm katılımlar, devam etmeden önce kabul etmeniz gereken" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Sihirbaz Ayarları" - customize_avatar: "Avatar'ınızı Özelleştirin" -# active: "Active" -# color: "Color" -# group: "Group" - clothes: "Kıyafet" - trim: "Süs" - cloud: "Püs" -# team: "Team" - spell: "Büyü" - boots: "Çizme" - hue: "Ton" - saturation: "Doyum" - lightness: "Parlaklık" - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/uk.coffee b/app/locale/uk.coffee index b192d9d28..63bfa040b 100644 --- a/app/locale/uk.coffee +++ b/app/locale/uk.coffee @@ -1,27 +1,28 @@ -module.exports = nativeDescription: "українська мова", englishDescription: "Ukrainian", translation: +module.exports = nativeDescription: "Українська", englishDescription: "Ukrainian", translation: home: slogan: "Навчіться програмувати, граючи у гру" - no_ie: "На жаль, CodeCombat не працює в IE8 чи більш старих версіях!" # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat не призначений для мобільних приладів і може не працювати!" # Warning that shows up on mobile devices - play: "Грати" # The big play button that just starts playing a level - old_browser: "Вибачте, але ваш браузер дуже старий для гри CodeCombat" # Warning that shows up on really old Firefox/Chrome/Safari + no_ie: "На жаль, CodeCombat не працює в IE8 та старіших версіях!" # Warning that only shows up in IE8 and older + no_mobile: "CodeCombat не призначений для мобільних пристроїв і може не працювати!" # Warning that shows up on mobile devices + play: "Грати" # The big play button that opens up the campaign view. + old_browser: "Вибачте, але Ваш браузер занадто старий для гри CodeCombat" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "Ви все одно можете спробувати, хоча навряд чи вийде" + ipad_browser: "Погана новина: CodeCombat не працює у браузері iPad. Хороша новина: наш спеціальний додаток iPad очікує на модерацію Apple." campaign: "Кампанія" for_beginners: "Для новачків" multiplayer: "Командна гра" # Not currently shown on home page for_developers: "Для розробників" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + or_ipad: "Або завантажте на iPad" nav: play: "Грати" # The top nav bar entry where players choose which levels to play -# community: "Community" + community: "Спільнота" editor: "Редактор" - blog: "Блог" + blog: "Блоґ" forum: "Форум" account: "Акаунт" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + profile: "Профіль" + stats: "Статистика" + code: "Код" admin: "Адміністратор" # Only shows up when you are an admin home: "На головну" contribute: "Співпраця" @@ -29,7 +30,7 @@ module.exports = nativeDescription: "українська мова", englishDesc about: "Про нас" contact: "Контакти" twitter_follow: "Фоловити" -# teachers: "Teachers" + teachers: "Учителям" modal: close: "Закрити" @@ -40,98 +41,98 @@ module.exports = nativeDescription: "українська мова", englishDesc diplomat_suggestion: title: "Допоможіть перекласти CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. - sub_heading: "Нам потрібні ваші мовні таланти." - pitch_body: "Ми створюємо CodeCombat англійською, але в нас вже є гравці по всьому світі. Багато хто з них хоче грати українською, але не говорить англійською, тому, якщо ви знаєте обидві мови, обміркуйте можливість стати Дипломатом і допомогти перекласти сайт CodeCombat та всі рівні українською." - missing_translations: "Поки ми не переклали все українською, ви будете бачити англійський текст там, де українська ще недоступна." + sub_heading: "Нам потрібні Ваші мовні таланти." + pitch_body: "Ми створюємо CodeCombat англійською, але в нас уже є гравці зі всього світу. Багато хто з них хоче грати українською, але не говорить англійською, тому, якщо Ви знаєте обидві мови, обміркуйте можливість стати Дипломатом і допомогти перекласти сайт CodeCombat та всі рівні українською." + missing_translations: "Поки ми не переклали все українською, Ви будете бачити англійський текст там, де українська ще не доступна." learn_more: "Дізнатися, як стати Дипломатом" subscribe_as_diplomat: "Записатися в Дипломати" play: - play_as: "Грати як" # Ladder page + play_as: "Грати за" # Ladder page spectate: "Спостерігати" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + players: "гравці" # Hover over a level on /play + hours_played: "годин зіграно" # Hover over a level on /play + items: "Предмети" # Tooltip on item shop button from /play + unlock: "Відкрити" # For purchasing items and heroes + confirm: "Підтвердити" + owned: "у власності" # For items you own + locked: "Заблоковано" + purchasable: "Можна придбати" # For a hero you unlocked but haven't purchased + available: "Доступно" + skills_granted: "Надані вміння" # Property documentation details + heroes: "Герої" # Tooltip on hero shop button from /play + achievements: "Досягнення" # Tooltip on achievement list button from /play + account: "Акаунт" # Tooltip on account button from /play + settings: "Налаштування" # Tooltip on settings button from /play + poll: "Опитування" # Tooltip on poll button from /play + next: "Далі" # Go from choose hero to choose inventory before playing a level + change_hero: "Змінити героя" # Go back from choose inventory to choose hero + choose_inventory: "Одягнути предмети" + buy_gems: "Придбати самоцвіти" + subscription_required: "Потрібен абонемет" + anonymous: "Гравець-анонім" level_difficulty: "Складність: " campaign_beginner: "Кампанія для початківців" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Оберіть свій рівень" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Ви можете грати у будь-який рівень з наведених нижче або обговорювати рівні на " - adventurer_forum: "форумі Шукачів пригод" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "... у якій ви навчитеся магії програмування." - campaign_dev: "Випадкові складніші рівні" - campaign_dev_description: "... в яких ви вивчите інтерфейс, одночасно роблячи щось складніше." + awaiting_levels_adventurer_prefix: "Ми випускаємо 5 рівнів на тиждень." # {change} + awaiting_levels_adventurer: "Увійди як Шукач пригод" + awaiting_levels_adventurer_suffix: "стань одним з перших, хто їх спробує." + adjust_volume: "Підлаштувати гучність" campaign_multiplayer: "Арени для мультиплеєра" - campaign_multiplayer_description: "... в яких ви програмуєте віч-на-віч із іншими гравцями." - campaign_player_created: "Рівні, створені гравцями" - campaign_player_created_description: "... у яких ви змагаєтесь у креативності із вашими друзями-<a href=\"/contribute#artisan\">Архітекторами</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" + campaign_multiplayer_description: "... в яких Ви програмуєте віч-на-віч із іншими гравцями." +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Ви робите великі успіхи! Розкажіть кому-небудь, як багато ви вивчили з CodeCombat." # {change} + email_invalid: "Невірна електронна адреса." + form_blurb: "Введіть їхні електронні адреси, і ми покажемо ім!" + form_label: "Електронна адреса" + placeholder: "електронна адреса" + title: "Досконала робота, Учень" login: - sign_up: "створити акаунт" + sign_up: "створення акаунту" log_in: "Увійти" logging_in: "Вхід в акаунт" log_out: "Вийти" - recover: "відновити акаунт" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Забули свій пароль?" + authenticate_gplus: "Авторизація G+" + load_profile: "Завантажити профіль G+" + finishing: "Завершення" + sign_in_with_facebook: "Увійти через Facebook" + sign_in_with_gplus: "Увійти через G+" + signup_switch: "Хочете створити акаунт?" signup: - create_account_title: "Створити акаунт, щоб зберегти прогрес" - description: "Це безкоштовно. Просто зробіть кілька простих кроків, щоб бути готовим до гри:" email_announcements: "Отримувати анонси на email" - coppa: "Ви старші 13 років або живете не в США" - coppa_why: "(Чому?)" creating: "Створення акаунта..." sign_up: "Реєстрація" log_in: "вхід з паролем" - social_signup: "Або Ви можете створити акаунт через Facebook або G+:" -# required: "You need to log in before you can go that way." + social_signup: "Або Ви можете створити акаунт через Facebook чи G+:" + required: "Вам потрібно увійти, щоби виконати цю дію." + login_switch: "Уже маєте акаунт?" recover: recover_account_title: "Відновити акаунт" send_password: "Надіслати пароль відновлення" -# recovery_sent: "Recovery email sent." + recovery_sent: "Лист для відновлення надіслано." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Основна" + secondary: "Додаткова" + armor: "Броня" + accessories: "Прикраси" + misc: "Різне" + books: "Книги" common: + back: "Назад" # When used as an action verb, like "Navigate backward" + continue: "Вперед" # When used as an action verb, like "Continue forward" loading: "Завантаження..." saving: "Збереження..." sending: "Надсилання..." send: "Надіслано" - cancel: "Відміна" + cancel: "Скасувати" save: "Зберегти" publish: "Опублікувати" create: "Створити" @@ -139,19 +140,37 @@ module.exports = nativeDescription: "українська мова", englishDesc fork: "Форк" play: "Грати" # When used as an action verb, like "Play next level" retry: "Повтор" + actions: "Дії" + info: "Інформація" + help: "Допомога" watch: "Стежити" - unwatch: "Нестежити" -# submit_patch: "Submit Patch" + unwatch: "Не стежити" + submit_patch: "Надіслати патч" + submit_changes: "Надіслати зміни" + save_changes: "Зберегти зміни" general: and: "та" - name: "Ім’я" -# date: "Date" + name: "Ім'я" + date: "Дата" body: "Тіло" version: "Версія" + pending: "Очікування" + accepted: "Прийнято" + rejected: "Відхилено" + withdrawn: "Відкликано" + submitter: "Відправник" + submitted: "Відправлено" commit_msg: "Доручити повідомлення" + review: "Огляд" version_history: "Історія" - version_history_for: "Версія історії для: " + version_history_for: "Історія версій для: " + select_changes: "Оберіть дві зміни нижче, щоб побачити відмінності." + undo_prefix: "Відмінити" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Повторити" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Попередній перегляд поточного рівня" result: "Результат" results: "Результати" description: "Опис" @@ -165,7 +184,7 @@ module.exports = nativeDescription: "українська мова", englishDesc when: "Коли" opponent: "Противник" rank: "Звання" - score: "Рахунок" + score: "Результат" win: "Перемога" loss: "Поразка" tie: "Нічия" @@ -173,276 +192,501 @@ module.exports = nativeDescription: "українська мова", englishDesc medium: "Середній" hard: "Важкий" player: "Гравець" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + player_level: "Рівень" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Воїн" + ranger: "Рейнджер" + wizard: "Чаклун" units: second: "Секунда" - seconds: "Секунди" + seconds: "Секунд" minute: "Хвилина" - minutes: "Хвилини" + minutes: "Хвилин" hour: "Година" - hours: "Години" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + hours: "Годин" + day: "день" + days: "днів" + week: "тиждень" + weeks: "тижнів" + month: "місяць" + months: "місяців" + year: "рік" + years: "років" play_level: done: "Готово" home: "На головну" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "Рівень" # Like "Level: Dungeons of Kithgard" + skip: "Пропустити" + game_menu: "Ігрове меню" guide: "Посібник" restart: "Перезавантажити" goals: "Цілі" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" + goal: "Ціль" + running: "Виконання..." + success: "Успішно!" + incomplete: "Незавершено" + timed_out: "Час очікування минув" + failing: "Невдало" action_timeline: "Лінія часу" - click_to_select: "Клікніть на юніті, щоб обрати його." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + click_to_select: "Клікніть на підрозділ, аби вибрати його." + control_bar_multiplayer: "Мультиплеєр" + control_bar_join_game: "Приєднатись до гри" + reload: "Перезавантажити " reload_title: "Перезавантажити весь код?" reload_really: "Ви впевнені, що хочете перезавантажити цей рівень і почати спочатку?" reload_confirm: "Перезавантажити все" + victory: "Перемога" victory_title_prefix: "" - victory_title_suffix: " закінчено" - victory_sign_up: "Підписатися на оновлення" - victory_sign_up_poke: "Хочете отримувати останні новини на email? Створіть безкоштовний акаунт, і ми будемо тримати вас у курсі!" + victory_title_suffix: " завершено" + victory_sign_up: "Підписатись на оновлення" + victory_sign_up_poke: "Хочете отримувати останні новини на email? Створіть безкоштовний акаунт, і ми будемо тримати Вас у курсі!" victory_rate_the_level: "Оцінити рівень: " # Only in old-style levels. - victory_return_to_ladder: "Повернутись до таблиці рівнів" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "Наступний рівень" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_return_to_ladder: "Повернутися до таблиці рівнів" + victory_play_continue: "Продовжити" + victory_saving_progress: "Триває збереження прогресу" victory_go_home: "На головну" # Only in old-style levels. victory_review: "Розкажіть нам більше!" # Only in old-style levels. victory_hour_of_code_done: "Ви закінчили?" - victory_hour_of_code_done_yes: "Так, я закінчив свою Годину Коду!" + victory_hour_of_code_done_yes: "Так, я закінчив Годину Коду!" + victory_experience_gained: "Отриманий досвід" + victory_gems_gained: "Отримані самоцвіти" + victory_new_item: "Новий предмет" + victory_viking_code_school: "Оце так! Це був важкий рівень, але Ви справились! Якщо Ви ще не розробник програмного забезпечення, Вам слід вже бути. Ви щойно пройшли швидку процедуру прийняття у Viking Code School, де Ви матимете змогу удосконалити Ваші навички до наступного рівня та стати професійним веб-розробником за 14 тижнів." + victory_become_a_viking: "Стати вікінгом" guide_title: "Посібник" - tome_minion_spells: "Закляття ваших міньонів" # Only in old-style levels. - tome_read_only_spells: "Закляття тільки для читання" # Only in old-style levels. + tome_minion_spells: "Заклинання Ваших міньонів" # Only in old-style levels. + tome_read_only_spells: "Заклинання тільки для читання" # Only in old-style levels. tome_other_units: "Інші юніти" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_run: "Виконати" + tome_cast_button_running: "Виконання" + tome_cast_button_ran: "Виконати" + tome_submit_button: "Підтвердити" + tome_reload_method: "Відновити початковий код цього методу" # Title text for individual method reload button. + tome_select_method: "Оберіть метод" + tome_see_all_methods: "Перегляньте всі методи, які Ви можете редагувати" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "Оберіть когось для " - tome_available_spells: "Доступні закляття" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" + tome_available_spells: "Доступні заклинання" + tome_your_skills: "Ваші вміння" + tome_help: "Допомога" + tome_current_method: "Поточний метод" + hud_continue_short: "Продовжити" + code_saved: "Код збережено" skip_tutorial: "Пропустити (esc)" -# keyboard_shortcuts: "Key Shortcuts" + keyboard_shortcuts: "Клавіатурні скорочення" loading_ready: "Готово!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" + loading_start: "Розпочати рівень" + problem_alert_title: "Виправте код" + problem_alert_help: "Допомога" time_current: "Зараз:" time_total: "Найбільше:" time_goto: "Перейти до:" - infinite_loop_try_again: "Спробувати щераз" + non_user_code_problem_title: "Не вдається завантажити рівень" + infinite_loop_title: "Виявлено вічний цикл" + infinite_loop_description: "Вихідний код побудови середовища не закінчив роботи. Певно він дуже повільний або містить вічний цикл. Або там може бути баґ. Ви можете запустити цей код заново або ж перезавантажити код у стан за замовчуванням. Якщо це не допомагає, будьте ласкаві, повідомте нас." + check_dev_console: "Ви також можете відкрити консоль розробника аби перевірити що ж там твориться." + check_dev_console_link: "(інструкції)" + infinite_loop_try_again: "Спробувати ще раз" infinite_loop_reset_level: "Почати рівень спочатку" - infinite_loop_comment_out: "Залишити коментарі до мого коду" - tip_toggle_play: "Перемикач грати/пауза командою Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ і Ctrl+] для перемотування та швидкого перемотування вперед." - tip_guide_exists: "Натисніть інструкцію вгорі сторінки для корисної інформації." - tip_open_source: "CodeCombat є 100% відкритим кодом!" - tip_beta_launch: "CodeCombat запустив бета версію в жовтні 2013." -# tip_think_solution: "Think of the solution, not the problem." - tip_theory_practice: "В теорії між теорією і практикою немає ніякої різниці. На практиці - є. - Йогі Берра" - tip_error_free: "Є два шляхи написання програм без помилок; але лише третій працює. - Алан Перліс" - tip_debugging_program: "Якщо налагодження є процесом усунення помилок, то програмування повинно бути процесом їх вставляння. - Едсгер В. Дейкстра" - tip_forums: "Заходіть на форум і скажіть нам що Ви думаєте!" - tip_baby_coders: "В майбутньому, навіть малюки будуть Архімагами." - tip_morale_improves: "Завантеження буде продовжуватись, поки мораль не покращиться." - tip_all_species: "Ми віримо у рівні можливості для вивчення пограмування усіх видів." + infinite_loop_comment_out: "Закоментувати мій код" + tip_toggle_play: "Перемикай між грою та паузою командою Ctrl+P." + tip_scrub_shortcut: "Ctrl+[ і Ctrl+] для перемотування назад та вперед." + tip_guide_exists: "Натисніть на Інструкцію вгорі сторінки, аби отримати корисну інформацію." + tip_open_source: "CodeCombat є абсолютно відкритим програмним забезпеченням!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "Бета-версія CodeCombat вийшла в жовтні 2013." + tip_think_solution: "Думай над рішенням, а не над проблемою." + tip_theory_practice: "У теорії між теорією і практикою немає ніякої різниці. На практиці - є. - Йоґі Берра" + tip_error_free: "Є два шляхи написання програм без помилок; але працює лише третій. - Алан Перліс" + tip_debugging_program: "Якщо налагодження є процесом усунення помилок, то програмування повинно бути процесом їх створення. - Едсґер В. Дейкстра" + tip_forums: "Заходьта на форум і розкажіть нам, що Ви думаєте!" + tip_baby_coders: "У майбутньому, навіть малюки будуть Архімагами." + tip_morale_improves: "Завантаження буде продовжуватися, поки мораль не покращиться." + tip_all_species: "Ми віримо у рівні можливості вивчення програмування для всіх." tip_reticulating: "Сітчасті голки." - tip_harry: "Так Чарівник, " - tip_great_responsibility: "З хорошими навичками кодування приходить відповідальність хорошого усунення помилок." - tip_munchkin: "Якщо ти не будеш їсти овощі, гном прийде до тебе поки ти спатимеш." - tip_binary: "Є лише 10 типів людей у світі: ті хто розуміють двійкову систему, і ті хто ні." - tip_commitment_yoda: "Програміст повинен мати глибоку прихильність, найсерйозніший розум. - Йода" - tip_no_try: "Робити. Чи не робити. .Це не спроба - Йода" - tip_patience: "Терпіння необхідно мати, юний падаван. - Йода" - tip_documented_bug: "Задокументована помилка не є помилко; це особливість." + tip_harry: "Так, Чарівнику, " + tip_great_responsibility: "З хорошим вмінням кодування приходить відповідальність хорошого усунення помилок." + tip_munchkin: "Якщо ти не будеш їсти овочі, гном прийде до тебе, як ти спатимеш." + tip_binary: "Є лише 10 типів людей у світі: ті, хто розуміє двійкову систему, і ті, хто ні." + tip_commitment_yoda: "Повинен програміст мати відданість глибоку, найсерйозніший розум. - Йода" + tip_no_try: "Роби. Або не роби. Немає 'спробую'. - Йода" + tip_patience: "Мати терпіння необхідно, юний Падаване. - Йода" + tip_documented_bug: "Задокументована помилка не є помилкою; це особливість." tip_impossible: "Багато речей здаються нездійсненними до тих пір, поки їх не зробиш. - Нельсон Мандела" - tip_talk_is_cheap: "Розмови нічого не варті. Покажи мені код. - Лінус Торвальдс" - tip_first_language: "Найбільш катастрофічною річчю яку ви коли-небудь вчили є Ваша перша мова програмування. - Алан Кей" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." -# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Налаштування персонажа" + tip_talk_is_cheap: "Розмови – ніщо. Покажи мені код. - Лінус Торвальдс" + tip_first_language: "Найбільш катастрофічною річчю, яку Ви коли-небудь вчили, є Ваша перша мова програмування. - Алан Кей" + tip_hardware_problem: "П: Скільки програмістів треба, аби замінити лампочку? В: Жодного, це апаратна проблема." + tip_hofstadters_law: "Закон Гофштадтера: Це в будь-якому разі займе більше часу, навіть якщо Ви берете до уваги закон Гофштадтера." + tip_premature_optimization: "Передчасна оптимізація – корінь усього зла. - Дональд Кнут" + tip_brute_force: "Якщо Ви сумніваєтеся, використовуйте перебір. - Кен Томсон" + tip_extrapolation: "Існує 2 типи людей: які можуть екстраполюватись від неповних даних..." + tip_superpower: "Написання коду - це найближче до суперсили, що у нас є." + tip_control_destiny: "Зі справді відкритим кодом у тебе є право контролювати свою власну долю. - Лінус Торвальдс" + tip_no_code: "Немає коду швидшого, ніж відсутній код." + tip_code_never_lies: "Код ніколи не бреше. Коментарі - іноді. — Ron Jeffries" + tip_reusable_software: "Щоб програмне забезпечення можна було використовувати повторно, треба, щоб його можна було використовувати." + tip_optimization_operator: "У кожній мові є свій оператор оптимізації. У більшості мов це ‘//’" + tip_lines_of_code: "Вимірювати прогрес програмування кількістю рядків коду - це як вимірювати прогрес літакобудування вагою. — Білл Ґейтс" + tip_source_code: "Я хочу змінити світ, але мені не дають первісний код." + tip_javascript_java: "Java має таке ж відношення до JavaScript'у, як віз до візерунка. - Кріс Гейлманн" + tip_move_forward: "Щоб ви не робили, продовжуйте рухатись вперед. - Мартін Лютер Кінг Мол." + tip_google: "Не можеш вирішити задачу? Заґуґли її!" + tip_adding_evil: "Додамо дрібочку зла." + tip_hate_computers: "Ось правда про людей, які вважають, що ненавидять комп'ютери. Що вони насправді ненавидять, це паршивий програмерів. - Ларрі Нівен" + tip_open_source_contribute: "Ви можете допомогти CodeCombat покращитися!" + tip_recurse: "Ітерація - від людини, рекурсія - від бога. - Л. Пітер Дойч" + tip_free_your_mind: "Нео, ти повинен усе подолати. Страх... сумніви і невіра. Звільни від них свій розум. - Морфіус" + tip_strong_opponents: "Навіть наймогутніший суперник має свою слабкість. - Ітачі Учіха" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "Інвентар" + save_load_tab: "Зберегти/Завантажити" + options_tab: "Параметри" + guide_tab: "Довідник" + guide_video_tutorial: "Відео-посібник" + guide_tips: "Поради" multiplayer_tab: "Мультиплеєр" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + auth_tab: "Вийти" + inventory_caption: "Екіпіруйте свого героя" + choose_hero_caption: "Оберіть героя, мову" + save_load_caption: "... та перегляньте історію" + options_caption: "Налаштування параметрів" + guide_caption: "Документація та поради" + multiplayer_caption: "Гра з друзями!" + auth_caption: "Збережіть свій поступ." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "Таблиця лідерів" + view_other_solutions: "Подивитись розв'язки інших" # {change} + scores: "Рейтинг" + top_players: "Сортувати гравців за" + day: "Сьогодні" + week: "Цього тижня" + all: "За увесь час" + time: "Час" + damage_taken: "Пошкоджень отримано" + damage_dealt: "Пошкоджень нанесено" + difficulty: "Складність" + gold_collected: "Золота зібрано" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "Екіпіруйте предмети" + equipped_item: "Екіпіровано" + required_purchase_title: "Потребує" + available_item: "Доступно" + restricted_title: "Обмежено" + should_equip: "(предмети одягаються подвійним кліком)" + equipped: "(екіпіровано)" + locked: "(заблоковано)" + restricted: "(обмежено для цього рівня)" + equip: "Одягнути" + unequip: "Зняти" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + buy_gems: + few_gems: "Кілька самоцвітів" + pile_gems: "Купа самоцвітів" + chest_gems: "Скриня самоцвітів" + purchasing: "Купування.." + declined: "Вашу карту відхилено" + retrying: "Помилка сервера, повторна спроба." + prompt_title: "Недостатньо самоцвітів" + prompt_body: "Хочете отримати ще?" + prompt_button: "Увійти до крамниці" + recovered: "Попередні покупки самоцвітів відновлені. Будь ласка, поновіть сторінку." + price: "x3500 / міс" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + subscribe: + comparison_blurb: "Відточіть свої навички завдяки підписці на CodeCombat!" + feature1: "Більше 60 основних рівней на просторах 4 світів" # {change} + feature2: "7 могутніх <strong>нових героїв</strong> з унікальними здібностями!" # {change} + feature3: "Більше 30 бонусних рівнів" # {change} + feature4: "<strong>3500 бонусних самоцвітів</strong> кожного місяця!" + feature5: "Навчальні відеоролики" + feature6: "Екслюзивна підтримка по електронній пошті" + feature7: "Приватні <strong>клани</strong>" + free: "Безкоштовно" + month: "місяць" + subscribe_title: "Взяти абонемент" + unsubscribe: "Скасувати абонемент" + confirm_unsubscribe: "Підтвердити відміну підписки" + never_mind: "Неважливо, Я Все Одно Тебе Люблю" + thank_you_months_prefix: "Дякуємо за підтримку нас протягом останніх" + thank_you_months_suffix: "місяців." + thank_you: "Дякуємо за підтримку CodeCombat." + sorry_to_see_you_go: "Шкода, що Ви йдете! Будь ласка, дайте нам знати, що ми могли б зробити краще." + unsubscribe_feedback_placeholder: "О, що ж ми зробили?" + parent_button: "Спитати у батьків" + parent_email_description: "Ми відправимо ім електронний лист, щоб вони могли придбати тобі підписку." + parent_email_input_invalid: "Введено невірну електронну адресу." + parent_email_input_label: "Електронна адреса батьків" + parent_email_input_placeholder: "Введи електронну адресу батьків" + parent_email_send: "Відправити лист" + parent_email_sent: "Лист відправлено!" + parent_email_title: "Яка в твоїх батьків електронна адреса?" + parents: "Батькам" + parents_title: "Ваша дитина вчитиметься програмувати." # {change} + parents_blurb1: "Разом з CodeCombat Ваша дитина писатиме реальний код. Почне з простих команд та поступово буде розвиватись до складніших тем." + parents_blurb1a: "Коп'ютерне програмування є необхідними вмінням, що ваша дитина беззаперечно використовуватиме у дорослому віці. До 2020 року 77% професій потребуватимуть базових навичок у програмному забезпечені, а програмісти надзвичайно потрібні у всьому світі. Чи знали ви, що Комп'ютерні Науки - це найбільш високооплачувана університетьська спеціальність?" + parents_blurb2: "За 9.99$ на місяць, вона отримуватиме нові завдання щотижня та персональні листи підтримки від професійних програмістів." # {change} + parents_blurb3: "Жодного ризику: 100% гарантія повернення грошей, легке скасування абонементу одним кліком." + payment_methods: "Платіжні методи" + payment_methods_title: "Платіжні методи, що приймаються" + payment_methods_blurb1: "Наразі ми приймаємо кредитні картник та Alpiay." + payment_methods_blurb2: "Якщо Вам необхідно використати інший спосіб оплати, будь ласка, зв'яжіться з нами." + stripe_description: "Щомісячний абонемент" + subscription_required_to_play: "Аби грати в цьому рівні потрібен абонемент." + unlock_help_videos: "Підпишіться, щоб відкрити усі навчальні відео." + personal_sub: "Особистий абонемент" # Accounts Subscription View below + loading_info: "Завантажується інформація про абонемент..." + managed_by: "Керівник - " + will_be_cancelled: "Буде скасовано" + currently_free: "Зараз у Вас безкоштовний абонемент" + currently_free_until: "Зараз у Вас безкоштовний абонемент до " + was_free_until: "У Вас був безкоштовний абонемент до " + managed_subs: "Керовані абонементи" + managed_subs_desc: "Додати абонементи для інших гравців (учнів, дітей тощо)" + managed_subs_desc_2: "Одержувачі повинні мати обліковий запис CodeCombat, пов'язаний з вказаною Вами адресою електронної пошти." + group_discounts: "Групові знижки" + group_discounts_1: "Ми також пропонуємо знижки для пакетних передплат." + group_discounts_1st: "1-ий абонемент (включає Ваш)" # {change} + group_discounts_full: "Повна ціна" + group_discounts_2nd: "2-11 абонементи" + group_discounts_20: "Знижка 20%" + group_discounts_12th: "Абонементи від 12-го" + group_discounts_40: "Знижка 40%" + subscribing: "Передплата..." + recipient_emails_placeholder: "Введіть ел. адреси для передплати, по одній в рядку." + subscribe_users: "Підписати користувачів" + users_subscribed: "Підписані користувачі:" + no_users_subscribed: "Користувачі не підписані, будь ласка, перевірте Ваші ел. адреси." + current_recipients: "Поточні отримувачі" + unsubscribing: "Скасування передплати..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + choose_hero: + choose_hero: "Оберіть героя" + programming_language: "Мова програмування" + programming_language_description: "Яку мову програмування Ви хочете використовувати?" + default: "Типова" + experimental: "Експериментальна" + python_blurb: "Проста, але потужна, чудово підходить як новачкам, так і експертам." + javascript_blurb: "Мова веб-сторінок. (Не плутати з Java.)" + coffeescript_blurb: "Покращений синтаксис JavaScript." + clojure_blurb: "Сучасний Lisp." + lua_blurb: "Мова ігрових сценаріїв." + io_blurb: "Проста, але дивна." + status: "Статус" + hero_type: "Тип" + weapons: "Зброя" + weapons_warrior: "Мечі – ближній бій, жодної магії" + weapons_ranger: "Стрілецька зброя – дистанційна атака, жодної магії" + weapons_wizard: "Жезли – дистанційна атака, магія" + attack: "Ушкодження" # Can also translate as "Attack" + health: "Здоров'я" + speed: "Швидкість" + regeneration: "Відновлення" + range: "Дистанція" # As in "attack or visual range" + blocks: "Блокує" # As in "this shield blocks this much damage" + backstab: "Зі спини" # As in "this dagger does this much backstab damage" + skills: "Вміння" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." + speed_1: "Рухається " + speed_2: " метрів на секунду." + available_for_purchase: "Можна придбати" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "Розблокується на рівні:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "Тільки певні герої можуть грати в цьому рівні." + + skill_docs: + writable: "записний" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "тільки читання" + action_name: "назва" + action_cooldown: "Триває" + action_specific_cooldown: "Замороження" + action_damage: "Ушкодження" + action_range: "Відстань" + action_radius: "Радіус" + action_duration: "Тривалість" + example: "Приклад" + ex: "типу" # Abbreviation of "example" + current_value: "Поточне значення" + default_value: "Типове значення" + parameters: "Параметри" + returns: "Повертає" + granted_by: "Забезпечує" + + save_load: + granularity_saved_games: "Збережено" + granularity_change_history: "Історія" options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." + general_options: "Загальні параметри" # Check out the Options tab in the Game Menu while playing a level + volume_label: "Гучність" + music_label: "Музика" + music_description: "Увімкнення та вимкнення фонової музики." editor_config: "Редактор налашт." editor_config_title: "Редактор налаштувань" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." - editor_config_keybindings_label: "Комбінаційї клавіш" - editor_config_keybindings_default: "За замовчуванням (Ace)" - editor_config_keybindings_description: "Додайте додаткові скорочення відомі Вам із загальних редакторів." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." - editor_config_invisibles_label: "Показати приховане" - editor_config_invisibles_description: "Відображення прихованого, такого як відступи та знаки табуляції." - editor_config_indentguides_label: "Показати відступи провідників" - editor_config_indentguides_description: "Відображення вертикальних ліній, щоб краще бачити відстань." - editor_config_behaviors_label: "Розумні привички" - editor_config_behaviors_description: "Автозаповнення дужок, фігурних дужок, та лапок." + editor_config_level_language_label: "Мова цього рівня" + editor_config_level_language_description: "Вкажіть мову програмування конкретно для цього рівня." + editor_config_default_language_label: "Типова мова програмування" + editor_config_default_language_description: "Вкажіть мову, яку хочете використовувати під час програмування нових рівнів." + editor_config_keybindings_label: "Комбінації клавіш" + editor_config_keybindings_default: "Типові (Ace)" + editor_config_keybindings_description: "Додайте додаткові скорочення, відомі Вам із інших редакторів." + editor_config_livecompletion_label: "Автодоповнення в реальному часі" + editor_config_livecompletion_description: "Показує поради автодоповнення під час друку." + editor_config_invisibles_label: "Показувати приховане" + editor_config_invisibles_description: "Відображення прихованого, як-то відступів та знаків табуляції." + editor_config_indentguides_label: "Показувати лінії відступів" + editor_config_indentguides_description: "Відображення вертикальних ліній відступів, щоби краще бачити відстань." + editor_config_behaviors_label: "Розумні навички" + editor_config_behaviors_description: "Автоматичне закриття дужок та лапок." about: why_codecombat: "Чому CodeCombat?" why_paragraph_1: "Хочете навчитися писати код? Вам не потрібні уроки. Вам потрібно писати багато коду і добре розважитись у цей час. " - why_paragraph_2_prefix: "Ось що таке програмування насправді. Це має бути весело. Не просто кумедно штибу" + why_paragraph_2_prefix: "Ось що таке програмування насправді. Це має бути весело. Не просто кумедно, штибу" why_paragraph_2_italic: "дивіться, я маю бейджик, " why_paragraph_2_center: "а весело - штибу" why_paragraph_2_italic_caps: "НІ, МАМО, Я МАЮ ПРОЙТИ РІВЕНЬ!" - why_paragraph_2_suffix: "Ось чому CodeCombat - мультиплеєрна гра, а не гейміфікований курс уроків. Ми не зупинимося, доки ви не включитеся на повну, і це чудово. " - why_paragraph_3: "Якщо ви плануєте бути залежним від якоїсь гри, оберіть цю - і перетворіться на одного з чарівників ери інформаційних технологій." -# press_title: "Bloggers/Press" -# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" -# press_paragraph_1_link: "press packet" -# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + why_paragraph_2_suffix: "Ось чому CodeCombat - мультиплеєрна гра, а не гейміфікований курс уроків. Ми не зупинимося, доки Ви не включитеся на повну, і це чудово. " + why_paragraph_3: "Якщо Ви плануєте бути залежним від якоїсь гри, оберіть цю - і перетворіться на одного з чарівників ери інформаційних технологій." + press_title: "Блоґерам/Пресі" + press_paragraph_1_prefix: "Хочете написати про нас? Не соромтесь завантажувати всі наші ресурси, які включено до нашого " + press_paragraph_1_link: "набору-для-преси" + press_paragraph_1_suffix: ". Всі логотипи та зображення можна використовувати, не зв'язуючись із нами напряму." + team: "Команда" + george_title: "Виконавчий директор" # {change} + george_blurb: "Бізнесмен" + scott_title: "Програміст" # {change} + scott_blurb: "Розумник" + nick_title: "Програміст" # {change} + nick_blurb: "Ґуру мотивації" + michael_title: "Програміст" + michael_blurb: "Сисадмін" + matt_title: "Програміст" # {change} + matt_blurb: "Велосипедист" + cat_title: "Головний ремісник" + cat_blurb: "Маг повітря" + josh_title: "Дизайнер гри" +# josh_blurb: "Floor Is Lava" + jose_title: "Музика" +# jose_blurb: "Taking Off" + retrostyle_title: "Ілюстрація" + retrostyle_blurb: "Ігри в стилі ретро" + + teachers: + title: "CodeCombat для вчителів" + intro_1: "CodeCombat - це онлайн гра, що вчить програмуванню. Студенти пишуть код на реальних мовах програмування." + intro_2: "Досвід не потрібен!" + free_title: "Скільки це коштує?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" + private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" + how_much_title: "Скільки коштує місячна передплата?" + how_much_1: "" + how_much_2: "Місячна передплата" + how_much_3: "коштує $9.99, та може бути скасована будь-коли." + how_much_4: "Крім цього, ми надаємо знижки для великих груп:" + how_much_5: "Ми надаємо знижку на разові закупівлі та річну передплату для груп, таких як клас або школа. Будь ласка, зв'яжіться з нами" + how_much_6: "для отримання більш детальної інформації." + more_info_title: "Де я можу знайти більше інформації?" + more_info_1: "Наш" + more_info_2: "вчительський форум" + more_info_3: "є гарним місцем для спілкування із колегами-педагогами, котрі використовують CodeCombat." + sys_requirements_title: "Системні вимоги" + sys_requirements_1: "Оскільки CodeCombat — це гра, для нормальної роботи він вимагає у комп'ютерів більше, ніж відео чи текстові посібники. Ми оптимізували його для швидкої роботи в усіх сучасних браузерах і на старіших машинах, щоб кожен міг грати. І ось наші підказки, як отримати від CodeCombat якнайбільше:" # {change} + sys_requirements_2: "Використовуйте новіші версії Chrome або Firefox." # {change} + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Зберегти нову версію" new_major_version: "Зберегти основну версію" + submitting_patch: "Відправка патчу..." cla_prefix: "Для збереження змін спочатку треба погодитись з нашим" cla_url: "CLA" cla_suffix: "." - cla_agree: "Я згоден" + cla_agree: "Я погоджуюсь" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Зв'язатися з CodeCombat" - welcome: "Ми раді вашому повідомленню! Скористайтеся цією формою, щоб надіслати email." - contribute_prefix: "Якщо ви зацікавлені у співпраці, завітайте на нашу " - contribute_page: "сторінку учасників" - contribute_suffix: "!" - forum_prefix: "Для будь-яких публичних обговорень, будь ласка, використовуйте " + welcome: "Ми раді вашому повідомленню! Скористайтеся цією формою, аби надіслати email." + forum_prefix: "Для будь-яких публічних обговорень, будь ласка, використовуйте " forum_page: "наш форум" forum_suffix: "." - send: "Надіслати фідбек" - contact_candidate: "Сконтактуватись з кандидатом" # Deprecated - recruitment_reminder: "Використовуйте цю форму щоб перейти до кандидатів з котрими Ви б хотіли провести співбесіду. Пам‘ятайте, що CodeCombat знімає 18% ЗП за перший рік. Плата проводиться за наймом співробітника і підлягає відшкодуванню протягом 90 днів якщо,працівник не залишить роботу. Часткова зайнятість,дистанційна робота, та наймані працівники не оплачуються, так само як інтерни." # Deprecated + faq_prefix: "Також у вас є" + faq: "ЧаПи" + subscribe_prefix: "Якщо вам потрібна допомога у проходженні рівня, будь ласка" + subscribe: "купіть підписку CodeCombat" + subscribe_suffix: "і ми будемо раді допомогти вам з вашим кодом." + subscriber_support: "Оскільки ви підписчик CodeCombat, ваші електронні листи отримають нашу пріорітетну підтримку." + screenshot_included: "Приєднано зняток." + where_reply: "Куди ми повинні відповісти?" + send: "Надіслати відгук" + contact_candidate: "Сконтактуватися з кандидатом" # Deprecated + recruitment_reminder: "Використовуйте цю форму, щоб перейти до кандидатів, з котрими Ви б хотіли провести співбесіду. Пам'ятайте, що CodeCombat знімає 18% ЗП за перший рік. Плата проводиться за наймом співробітника і підлягає відшкодуванню протягом 90 днів якщо працівник не залишить роботу. Часткова зайнятість, дистанційна робота та наймані працівники не оплачуються, так само як інтерни." # Deprecated account_settings: title: "Налаштування акаунта" @@ -450,244 +694,304 @@ module.exports = nativeDescription: "українська мова", englishDesc autosave: "Зміни зберігаються автоматично" me_tab: "Я" picture_tab: "Аватар" -# upload_picture: "Upload a picture" + delete_account_tab: "Вилучити свій акаунт" + wrong_email: "Неправильний email" +# wrong_password: "Wrong Password" + upload_picture: "Відвантажити зображення" + delete_this_account: "Вилучити цей акаунт назовсім" + god_mode: "Режим Бога" password_tab: "Пароль" emails_tab: "Email-адреси" admin: "Aдмін" new_password: "Новий пароль" new_password_verify: "Підтвердження паролю" + type_in_email: "Введіть свій email, щоб підтвердити вилучення" # {change} +# type_in_password: "Also, type in your password." email_subscriptions: "Email-підписки" -# email_subscriptions_none: "No Email Subscriptions." + email_subscriptions_none: "Жодних підписок." email_announcements: "Оголошення" email_announcements_description: "Отримувати електронні листи про останні новини CodeCombat." - email_notifications: "Нотифікації" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." + email_notifications: "Сповіщення" + email_notifications_summary: "Контроль персоналізованих автоматичних email-сповіщень щодо Вашої активності у CodeCombat." + email_any_notes: "Будь-які сповіщення" + email_any_notes_description: "Вимкніть, аби заборонити сповіщення на email." + email_news: "Новини" + email_recruit_notes: "Вакансії" + email_recruit_notes_description: "Якщо ти граєш дуже добре, ми можемо запропонувати тобі (кращу) роботу." contributor_emails: "Підписки за класами учасників" contribute_prefix: "Нам потрібні люди, які приєднаються до нашої команди! Зайдіть на " contribute_page: "сторінку учасників," contribute_suffix: " щоб дізнатися більше." email_toggle: "Обрати все" - error_saving: "Помилка при збереженні" + error_saving: "Помилка збереження" saved: "Зміни збережено" password_mismatch: "Паролі не збігаються." -# password_repeat: "Please repeat your password." - job_profile: "Профіль роботи" # Rest of this section (the job profile stuff and wizard stuff) is deprecated - job_profile_approved: "Ваш робочий пофіль буде затверджений CodeCombat. Роботодавці зможуть бачити його якщо він буде відмічений як активний, або він не зазнає змін протягом 4 тижнів." - job_profile_explanation: "Привіт! Заповніть це і ми з Вами зв‘яжемось знайшовши для Вас роботу розробника ПЗ." - sample_profile: "Дивитись зразок профілю" + password_repeat: "Будь ласка, повторіть пароль." + job_profile: "Робочий профіль" # Rest of this section (the job profile stuff and wizard stuff) is deprecated + job_profile_approved: "Ваш робочий профіль буде затверджено CodeCombat. Роботодавці зможуть бачити його, якщо він буде відмічений як активний або він не зазнає змін протягом 4 тижнів." + job_profile_explanation: "Привіт! Заповніть це і ми зв'яжемось з Вами, знайшовши для Вас роботу розробника ПЗ." + sample_profile: "Дивитися зразок профілю" view_profile: "Переглянути Ваш профіль" - wizard_tab: "Персонаж" - wizard_color: "Колір одягу персонажа" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." + keyboard_shortcuts: + keyboard_shortcuts: "Клавіатурні скорочення" + space: "Пробіл" + enter: "Enter" +# press_enter: "press enter" + escape: "Escape" + shift: "Shift" + run_code: "Виконати поточний код." + run_real_time: "Виконати в реальному часі." + continue_script: "Продовжити поточний скрипт." + skip_scripts: "Пропустити усі скрипти, які можна пропустити." + toggle_playback: "Перемикач гри/паузи." + scrub_playback: "Перемотування назад і вперед у часі." + single_scrub_playback: "Покадрове перемотування назад і вперед." + scrub_execution: "Перескочити через виконання поточного заклинання." + toggle_debug: "Перемикач відлагодження." + toggle_grid: "Перемикач сітки." + toggle_pathfinding: "Перемикач накладання шляху." + beautify: "Зроби код красивішим, стандартизуючи його форматування." + maximize_editor: "Згортання/розширення редактора коду." community: main_title: "Спільноти CodeCombat" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + introduction: "Ознайомтеся нижче зі способами, у які Ви можете долучитися, й оберіть те, що виглядає найбільш весело. Ми з нетерпінням чекаємо роботи з Вами!" + level_editor_prefix: "Використовуйте " + level_editor_suffix: "CodeCombat, щоб створювати та редагувати рівні. Користувачі створють нові рівні для своїх учнів, друзів, хакатонів, студентів і братів-сестер. Якщо створення нового рівня звучить страшно, Ви можете почати, зробивши форк одного з наших!" + thang_editor_prefix: "Юніти у грі ми називаємо 'об'єкти'. Використовуйте " + thang_editor_suffix: ", щоб модифікувати вихідне художнє оформлення CodeCombat. Дозвольте юнітам кидати снаряди, розверніть напрямок анімації, змініть очки життя юніта або завантажте власних векторних ельфів." + article_editor_prefix: "Бачили помилку у якомусь з наших доків? Хочете зробити якісь інструкції до своїх творінь? Огляньте " + article_editor_suffix: "і допоможіть гравцям CodeCombat отримати максимум зі свого ігрового часу." + find_us: "Шукайте нас на цих сайтах" + social_blog: "Читайте наш блоґ на Sett" + social_discource: "Приєднайтеся до обговорення на форумі" + social_facebook: "Вподобайте CodeCombat на Facebook" + social_twitter: "Слідкуйте за CodeCombat у Twitter" + social_gplus: "Приєднайтесь до CodeCombat у Google+" + social_hipchat: "Напишіть нам у публічній кімнаті CodeCombat HipChat" + contribute_to_the_project: "Взяти участь у розробці" + + clans: + clan: "Клан" + clans: "Клани" + new_name: "Назва нового клану" + new_description: "Опис нового клану" + make_private: "Зробити клан приватним" + subs_only: "лише для підписчиків" + create_clan: "Створити новий клан" +# private_preview: "Preview" + public_clans: "Публічні клани" + my_clans: "Мої клани" + clan_name: "Назва клану" + name: "Ім'я" + chieftain: "Отаман" + type: "Тип" + edit_clan_name: "Змінити назву клану" + edit_clan_description: "Змінити опис клану" + edit_name: "Змінити ім'я" + edit_description: "Змінити опис" + private: "(закритий)" + summary: "Загалом" + average_level: "Середній рівень" + average_achievements: "Середні досягнення" + delete_clan: "Видалити клан" + leave_clan: "Покинути клан" + join_clan: "Приєднатись до клану" + invite_1: "Запрошення:" + invite_2: "*Запросіть гравців до цього Клану, виславши дане посилання." + members: "Учасники" + progress: "Поступ" + not_started_1: "не розпочато" + started_1: "розпочато" + complete_1: "завершено" +# exp_levels: "Expand levels" + rem_hero: "Вилучити героя" + status: "Статус" + complete_2: "Завершено" + started_2: "Розпочато" + not_started_2: "Не розпочато" + view_solution: "Натисніть аби переглянути рішення." + latest_achievement: "Останнє досягнення" + playtime: "Тривалість гри" + last_played: "Остання гра" classes: archmage_title: "Архімаг" archmage_title_description: "(Програміст)" + archmage_summary: "Якщо Ви зацікавлені в програмуванні навчальних ігор, станьте архімагом аби допомогти нам створювати CodeCombat!" artisan_title: "Ремісник" artisan_title_description: "(Створювач рівнів)" + artisan_summary: "Створюйте і поширюйте рівні для себе і своїх друзів. Стань ремісником, щоб опанувати мистецтво навчання інших програмуванню." adventurer_title: "Шукач пригод" adventurer_title_description: "(Тестувальник рівнів)" + adventurer_summary: "Отримуйте наші нові рівні (навіть вміст передплат) безкоштовно на тиждень раніше і допомагайте нам шукати баґи до публічного випуску." scribe_title: "Писар" scribe_title_description: "(Редактор статей)" + scribe_summary: "Хороший код потребує хорошої документації. Пишіть, редагуйте, та покращуйте документацію, яку побачать мільйони гравців зі всього світу." diplomat_title: "Дипломат" diplomat_title_description: "(Перекладач)" - ambassador_title: "Посланець" + diplomat_summary: "За допомогою наших дипломатів CodeCombat локалізовано більш ніж 45 мовами. Ви також можете допомогти з перекладом." + ambassador_title: "Посол" ambassador_title_description: "(Підтримка)" + ambassador_summary: "Вгамовуйте користувачів нашого форуму і скеровуйте тих, хто має запитання. Наші посли — представники CodeCombat у світі." editor: main_title: "Редактори CodeCombat" article_title: "Редактор статей" thang_title: "Редактор об'єктів" level_title: "Редактор рівнів" -# achievement_title: "Achievement Editor" + achievement_title: "Редактор досягнень" + poll_title: "Редактор опитувань" back: "Назад" revert: "Повернутись" revert_models: "Моделі повернення" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" - fork_title: "Нова версія Форк" - fork_creating: "Створення Форк..." -# generate_terrain: "Generate Terrain" + pick_a_terrain: "Обрати лашдшафт" + dungeon: "Темниця" + indoor: "Кімната" + desert: "Пустеля" + grassy: "Подвір'я" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Малий" + large: "Великий" + fork_title: "Форк нової версії" + fork_creating: "Створення форку..." + generate_terrain: "Згенерувати ландшафт" more: "Більше" - wiki: "Wiki" + wiki: "Вікі" live_chat: "Online чат" - level_some_options: "Деякі опції?" + thang_main: "Головне" + thang_spritesheets: "Таблиці спрайтів" + thang_colors: "Кольори" + level_some_options: "Якісь опції?" level_tab_thangs: "Об'єкти" level_tab_scripts: "Скрипти" level_tab_settings: "Налаштування" level_tab_components: "Компоненти" level_tab_systems: "Системи" -# level_tab_docs: "Documentation" + level_tab_docs: "Документація" level_tab_thangs_title: "Поточні об'єкти" level_tab_thangs_all: "Усі" level_tab_thangs_conditions: "Початковий статус" level_tab_thangs_add: "Додати об'єкти" +# level_tab_thangs_search: "Search thangs" + add_components: "Додати коментарі" + component_configs: "Налаштування компонента" + config_thang: "Подвійний клік для конфігуровання об'єктів" delete: "Видалити" - duplicate: "Копіювати" -# rotate: "Rotate" + duplicate: "Дублювати" + stop_duplicate: "Зупинити дублювання" + rotate: "Повернути" level_settings_title: "Налаштування" level_component_tab_title: "Поточні компоненти" level_component_btn_new: "Створити новий компонент" level_systems_tab_title: "Поточна система" level_systems_btn_new: "Створити нову систему" level_systems_btn_add: "Додати систему" - level_components_title: "Повернутись до всіх об‘єктів" + level_components_title: "Повернутись до всіх об'єктів" level_components_type: "Тип" level_component_edit_title: "Редагувати компонент" - level_component_config_schema: "Config Schema" + level_component_config_schema: "Схема конфігурації" level_component_settings: "Налаштування" level_system_edit_title: "Редагувати систему" create_system_title: "Створити нову систему" new_component_title: "Створити новий компонент" new_component_field_system: "Система" new_article_title: "Створити нову статтю" - new_thang_title: "Створити новий тип об‘єкта" + new_thang_title: "Створити новий тип об'єкта" new_level_title: "Створити новий рівень" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" + new_article_title_login: "Увійдіть, щоб створити нову статтю" + new_thang_title_login: "Увійдіть, щоб створити новий тип об'єктів" + new_level_title_login: "Увійдіть, щоб створити новий рівень" + new_achievement_title: "Створити нове досягнення" + new_achievement_title_login: "Увійдіть, щоб створити нове досягнення" + new_poll_title: "Створити нове опитування" + new_poll_title_login: "Увійдіть, щоб створити нове опитування" article_search_title: "Шукати статті тут" - thang_search_title: "Шукати типи об‘єктів тут" + thang_search_title: "Шукати типи об'єктів тут" level_search_title: "Шукати рівні тут" -# achievement_search_title: "Search Achievements" + achievement_search_title: "Шукати досягнення" + poll_search_title: "Шукати опитування" read_only_warning2: "Примітка: Ви не можете зберегти ніякі зміни, оскільки Ви не зареєструвались." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" -# pop_i18n: "Populate I18N" + no_achievements: "Для цього рівня ще не додано жодних досягнень." + achievement_query_misc: "Ключове досягнення випадає з різного" + achievement_query_goals: "Ключове досягнення випадає з цілей рівня" + level_completion: "Рівень завершено" + pop_i18n: "Додати I18N" + tasks: "Завдання" + clear_storage: "Очистити свої локальні зміни" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: - edit_btn_preview: "Анонс" + edit_btn_preview: "Перегляд" edit_article_title: "Редагувати статтю" + polls: + priority: "Пріоритет" + contribute: page_title: "Співпраця" - character_classes_title: "Класи персонажів" - introduction_desc_intro: "Ми покладаємо великі надії на CodeCombat." - introduction_desc_pref: "Ми хочемо створити місце, де збиралися б програмісти найрізноманітніших спеціалізацій, абі вчитись та грати разом. Хочемо знайомити інших з неймовірним світом програмування та відображувати найкращі частини спільноти. Ми не можемо і не хочемо робити це самі, бо такі проекти, як GitHub, Stack Overflow або Linux стали видатними саме завдяки людям, що використовують і будують їх. Через це" - introduction_desc_github_url: "код CodeCombat повністю вікдритий" - introduction_desc_suf: ", і ми пропонуємо вам усі можливі шляхи взяти участь у розробці й перетворити цей проект на не тільки наш. але й ваш теж." - introduction_desc_ending: "Сподіваємось, ви станете частиною нашої команди!" - introduction_desc_signature: "- Нік, Джордж, Скотт, Майкл, Джеремі та Глен" + intro_blurb: "CodeCombat відкритий на 100%! Сотні гравців допомогли нам перетворити цю гру в те, чим вона є сьогодні. Приєднуйтесь до нас і зробіть свій внесок у місію CodeCombat навчити світ програмувати!" alert_account_message_intro: "Привіт!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "Зацікавлений у роботі над ігровою графікою, дизайном інтерфейсу, організацією баз даних та серверу, мультиплеєром, фізокю, звуком або продукційністю ігрового движка? Мрієш допомогти у створенні гри, яка навчить інших того, у чому ви профі? У нас багато роботи, і якщо ти досвідчений програміст і хочеш розробляти CodeCombat, цей клас для тебе. Ми будемо щасливі бачити, як ти створюєш найкращу в світі гру для програмістів. " - archmage_introduction: "Однією з найкращих частин створення ігор є те, що вони синтезують так багато різноманітних речей. Графіка, звук, з‘єднання з мережею у реальному часі, соціальні мережі, і ,звичайно, багато з найбільш поширених аспектів програмування, від управління низькорівневими базами даних, і адміністративної підтримки сервера до користувацького зовнішнього вигляду та побудови інтерфейсу. Тут є ще багато до виконання, і якщо ти досвідчений програміст з пристрастним бажанням зануритись у закаулки CodeCombat, цей розділ скоріше за все для Вас. Ми з радістю приймем Вашу допомогу у побудові найкращої з усіх гри для програмування." + alert_account_message: "Щоб підписатися на email'и класу, Ви спершу маєте увійти." + archmage_introduction: "Однією з найкращих частин створення ігор є те, що вони синтезують так багато різноманітних речей. Графіка, звук, з'єднання з мережею у реальному часі, соціальні мережі, і, звичайно, багато з найбільш поширених аспектів програмування, від управління низькорівневими базами даних і адміністративної підтримки сервера до користувацького зовнішнього вигляду та побудови інтерфейсу. Тут є ще багато до виконання, і якщо Ви досвідчений програміст із пристрасним бажанням зануритися у нетрі CodeCombat, цей розділ скоріше за все для Вас. Ми з радістю приймемо Вашу допомогу у побудові найкращої з усіх гри для програмування." class_attributes: "Ознаки класу" archmage_attribute_1_pref: "Навики у " - archmage_attribute_1_suf: ", або бажання навчитись. Більшість нашого коду написана написана на цій мові. Якщо Ви фанат Ruby або Python, Ви будете почуватись як у дома. Це JavaScript, але з приємнішим синтаксисом." + archmage_attribute_1_suf: ", або бажання навчитись. Більшість нашого коду написана написана на цій мові. Якщо Ви фанат Ruby або Python, Ви будете почуватись, як у дома. Це JavaScript, але з приємнішим синтаксисом." archmage_attribute_2: "Деякий досвід програмування та власна ініціатива. Ми допоможемо Вам зорієнтуватись, але ми не можемо витрачати багато часу для Вашого навчання." how_to_join: "Як приєднатися" - join_desc_1: "Кожен може допомогти! Заходь на наш " - join_desc_2: ", щоб почати та постав позначку в чек-боксі нижче, щоб оголосити себе відважним архімагом та отримувати останні новини по е-мейл. Хочеш поспілкуватися про те, що саме робити й як включитися в роботу найглибше?" - join_desc_3: "або знайдіть нас у нашій " + join_desc_1: "Кожен може допомогти! Заходьте на наш " + join_desc_2: ", щоб почати, та поставте позначку в чек-боксі нижче, щоб оголосити себе відважним архімагом та отримувати останні новини електронною поштою. Хочете поспілкуватися про те, що саме робити і як включитися в роботу найглибше? " + join_desc_3: " або знайдіть нас у нашій " join_desc_4: "- і ми вирішимо, з чого почати!" join_url_email: "Напишіть нам" join_url_hipchat: "публічній HipChat кімнаті" - more_about_archmage: "Дізнатися, як стати Архімагом" archmage_subscribe_desc: "Отримувати листи з анонсами та новими можливостями для розробки." - artisan_summary_pref: "Хочеш розробляти дизайн рівнів та розширювати арсенал CodeCombat? Люди проходять наші рівні в швидчому темпі ніж ми встигаємо їх створювати! На даний час, наш редактор рівнів є скелетом, тому будь обережним. Створення рівнів буде захоплюват і тягнути за собою. Якщо ти маєш уявлення про кампанії охоплюючі for-loops до" - artisan_summary_suf: "тоді цей клас для тебе." - artisan_introduction_pref: "Ми повинні будувати додаткові рівні! Люди вимагатимуть більше контенту, і ми можемо лише будувати скільки самі зможемо. Саме зараз, Вашою робочою зоною є рівень один; наш редактор рівнів ледве використовується навіть його творцями, тож будьте обережними. Якщо ти маєш уявлення про кампанії охоплюючі for-loops до" - artisan_introduction_suf: ", тоді цей клас для тебе." - artisan_attribute_1: "Будь-який досвід у створені контенту такого як цей буде добрим, так як і використання редакторів рівнів Blizzard. Але не вимається!" - artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." -# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" -# artisan_join_desc: "Use the Level Editor in these steps, give or take:" -# artisan_join_step1: "Read the documentation." -# artisan_join_step2: "Create a new level and explore existing levels." -# artisan_join_step3: "Find us in our public HipChat room for help." -# artisan_join_step4: "Post your levels on the forum for feedback." - more_about_artisan: "Дізнатися, як стати Ремісником" + artisan_introduction_pref: "Ми повинні будувати додаткові рівні! Люди вимагатимуть більше контенту, і ми можемо лише будувати, скільки самі зможемо. Саме зараз, Вашою робочою зоною є рівень один; наш редактор рівнів ледве використовується навіть його творцями, тож будьте обережними. Якщо Ви маєте бачення кампаній, що простягають цикли 'for' на " + artisan_introduction_suf: ", тоді цей клас для Вас." + artisan_attribute_1: "Добрим буде будь-який досвід у створені контенту такого, як цей, так само як і використання редакторів рівня Blizzard. Але не вимається!" + artisan_attribute_2: "Прагнення дуже багато багато тестувати і повторювати. Щоб робити гарні рівні, Вам треба дати їх іншим, подивитися, як вони грають, і бути готовими багато-чого виправляти." + artisan_attribute_3: "Витривалість на рівні Шукача пригод, увесь час. Наш Редактор рівнів дуже не остаточний, і в користуванні розчаровує. Вас було попереджено!" + artisan_join_desc: "Використовуйте Редактор рівнів на цих кроках, більше чи менше:" + artisan_join_step1: "Читайте документацію." + artisan_join_step2: "Створюйте новий рівень і досліджуйте існуючі рівні." + artisan_join_step3: "Просіть у нас допомоги у публічній HipChat." + artisan_join_step4: "Публікуйте свої рівні на форумі, щоб отримати відгуки." artisan_subscribe_desc: "Отримувати листи з анонсами та новинами про вдосконалення редактора рівнів." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" + adventurer_introduction: "Давайте будемо чесними щодо Вашої ролі: Ви танк. Вам доведеться мати багато втрат. Нам потрібні люди, щоб випробовувати нові рівні і допомагати визначати, що можна покращити. Це буде неймовірно боляче; створення хороших ігор - це довгий процес, і ніхто не проходить його з першого разу. Якшо Ви можеш терпіти і маєте великий запас витривалості, то цей клас для Вас." + adventurer_attribute_1: "Спрага до навчання. Ви хочете навчитися кодити і ми хочемо навчити Вас писати код. Хоча у цьому випадку Ви, напевно, вчитимете значно більше." + adventurer_attribute_2: "Харизма. Будьте лагідні, але переконливі щодо того, що потребує покращення, і висловлюйте пропозиції, як це зробити." + adventurer_join_pref: "Знайдіть собі до пари (або звербуйте!) Ремісника і працюйте з ним або поставте галочку нижче, щоб отримувати електронні листи, коли з'являтимуться нові рівні для тестування. Ми також будемо робити дописи про рівні, які треба оглянути, у наших мережах, зокрема на " adventurer_forum_url: "наш форум" -# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" - more_about_adventurer: "Дізнатися, як стати Шукачем пригод" + adventurer_join_suf: "тому, якщо Вам краще отримувати сповіщення таким чином, підпишіться там!" adventurer_subscribe_desc: "Отримувати листи, коли з'являються нові рівні для тестування." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " -# scribe_introduction_url_mozilla: "Mozilla Developer Network" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." -# contact_us_url: "Contact us" -# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" - more_about_scribe: "Дізнатися, як стати Писарем" + scribe_introduction_pref: "CodeCombat не збирається залишатися просто набором рівнів. Він також міститиме джерело знань, вікі з принципами програмування, з якою зчеплені рівні. Таким чином замістьо того, щоб кожен Ремісник детально пояснював, що таке оператор порівняння, він зможе просто поставити у своєму рівневі посилання на статтю, у якій описано все для настанови гравцям. Щось на зразок того, що створили " + scribe_introduction_url_mozilla: "Mozilla Developer Network" + scribe_introduction_suf: ". Якщо Ваше уявлення про веселощі промовляє через принципи програмування у формі Markdown, тоді цей клас для Вас." + scribe_attribute_1: "Вміння писати - це, по суті, все, що Вам треба. Не тільки знати граматику і правопис, але й могти розкривати складні ідеї іншим." + contact_us_url: "Зв'яжіться з нами" + scribe_join_description: ", розкажіть нам трохи про себе, свій досвід програмування і про які речі Ви хотіли б писати. З цього ми почнемо!" scribe_subscribe_desc: "Отрумивати листи з анонсами щодо написання статтей." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." -# diplomat_introduction_pref: "So, if there's one thing we learned from the " -# diplomat_launch_url: "launch in October" -# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." -# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." -# diplomat_join_pref_github: "Find your language locale file " + diplomat_introduction_pref: "Отож, якщо є одна річ, яку ми вивчили з часу " + diplomat_launch_url: "запуску в жовтні" + diplomat_introduction_suf: ", то це те, що є значний інтерес до CodeCombat в інших країнах! Ми формуємо загін перекладачів, охочих до перетворення одного набору слів на інший набір слів, щоб CodeCombat став якнайдоступнішим в усьому світі. Якщо Вам подобається захоплено поглядати на майбутній контент і якнайшвидше доносити його до своїх співвітчизників, тоді цей клас, напевно, для Вас." + diplomat_attribute_1: "Вільне знання англійської та мови, на яку Ви хочете перекладати. При поясленні складних ідей важливо мати глибоке розуміння обох мов!" + diplomat_i18n_page_prefix: "Ви можете почати переклад рівнів, перейшовши на нашу " + diplomat_i18n_page: "сторінку перекладу" + diplomat_i18n_page_suffix: ", або інтерфейсу та сайту перейшовши на GitHub." + diplomat_join_pref_github: "Знайдіть файл своєї мови " diplomat_github_url: "на GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" - more_about_diplomat: "Дізнатися, як стати Дипломатом" + diplomat_join_suf_github: ", відредагуйте його та надішліть запит на злиття (pull request). Також поставте прапорець нижче, аби слідкувати за новинами локалізації!" diplomat_subscribe_desc: "Отримувати листи про розробки i18n та нові рівні для перекладу." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." -# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" -# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" - more_about_ambassador: "Дізнатися, як стати Посланцем" + ambassador_introduction: "Ми будуємо спільноту, а Ви - зв'язки. У нас є чати Olark, електронні скриньки і соціальні мережі з багатьма людьми для спілкування і допомоги у знайомстві з грою на навчанні. Якщо Ви хочете допомагати людям залучатися, веселитися і тримати руку на пульсі CodeCombat та його шляху, тоді цей клас, напевно, для Вас." + ambassador_attribute_1: "Комунікативні навички. Могти ідентифікувати проблеми, з якими стикаються гравці, і могти допомогти з їх вирішенням. Також тримати решту з нас в курсі того, що кажуть гравці, що їм подобається, а що ні, і чого вони хочуть ще більше!" + ambassador_join_desc: ", розкажіть нам трохи про себе, що Ви робили і що б Ви зацікавлені були робити. З цього й почнемо!" + ambassador_join_note_strong: "Примітка" + ambassador_join_note_desc: "Один з наших пріоритетів - розробити мультиплеєр, у якому гравці, що мають труднощі з проходженням рівнів, зможуть викликати чарівників вищого рівня собі на допомогу. Ось тут Посланці і зможуть виконувати свою роботу. Ми будемо на зв'язку!" ambassador_subscribe_desc: "Отримувати листи з новинами щодо підтримки користувачів та розробки мультиплеєра." - changes_auto_save: "Зміни зберігаються автоматично, коли ви ставите позначку у чекбоксі." + changes_auto_save: "Зміни зберігаються автоматично, коли Ви ставите позначку у чекбоксі." diligent_scribes: "Наші старанні Писарі:" powerful_archmages: "Наші могутні Архімаги:" creative_artisans: "Наші талановиті Ремісники:" @@ -695,226 +999,280 @@ module.exports = nativeDescription: "українська мова", englishDesc translating_diplomats: "Наші перекладачі - Дипломати:" helpful_ambassadors: "Наші незамінні Посланці:" -# ladder: -# please_login: "Please log in first before playing a ladder game." -# my_matches: "My Matches" -# simulate: "Simulate" -# simulation_explanation: "By simulating games you can get your game ranked faster!" -# simulate_games: "Simulate Games!" -# simulate_all: "RESET AND SIMULATE GAMES" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" -# battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " -# summary_wins: " Wins, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" -# rank_submitting: "Submitting..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" -# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." -# choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" -# tutorial_play: "Play Tutorial" -# tutorial_recommended: "Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" -# tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" -# warmup: "Warmup" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + ladder: + please_login: "Будь ласка, увійдіть до Вашого акаунту перед грою у драбині." + my_matches: "Мої поєдинки" + simulate: "Моделювати" + simulation_explanation: "Моделюючи ігри, Ви можете швидше отримати оцінку своєї гри!" + simulate_games: "Моделювати ігри!" + simulate_all: "СКИНУТИ ТА МОДЕЛЮВАТИ ІГРИ" + games_simulated_by: "Ігор модельовано Вами:" + games_simulated_for: "Ігор модельовано для Вас:" + games_simulated: "Ігор модельовано" + games_played: "Ігор зіграно" + ratio: "Співвідношення" + leaderboard: "Таблиця лідерів" + battle_as: "Бій за " + summary_your: "Твої " + summary_matches: "Поєдинки - " + summary_wins: " Перемоги, " + summary_losses: " Поразки" + rank_no_code: "Нема нового коду для оцінки" + rank_my_game: "Оцінити мою гру!" + rank_submitting: "Відправлення..." + rank_submitted: "Відправлено для оцінки" + rank_failed: "Не вдалося оцінити" + rank_being_ranked: "Гра оцінюється" + rank_last_submitted: "відправлено " + help_simulate: "Допомогти моделювати ігри?" + code_being_simulated: "Ваш новий код моделюється іншими гравцями для оцінки. Буде оновлюватися із новими матчами." + no_ranked_matches_pre: "Немає нових матчів для " + no_ranked_matches_post: " команди! Зіграйте проти супротивників та поверніться для отримання оцінки Вашої гри." + choose_opponent: "Оберіть противника" + select_your_language: "Оберіть мову!" + tutorial_play: "Пройти навчання" + tutorial_recommended: "Рекомендовано, якщо Ви взагалі раніше не грали" + tutorial_skip: "Пропустити навчання" + tutorial_not_sure: "Не впевнені, що відбувається?" + tutorial_play_first: "Спочатку пройти навчання." + simple_ai: "Простий ШІ" + warmup: "Розігрів" + friends_playing: "Дружня гра" + log_in_for_friends: "Увійдіть та грайте за своїх друзів!" + social_connect_blurb: "Приєднайтесь та грайте проти Ваших друзів!" + invite_friends_to_battle: "Запросіть своїх друзів приєднатися до Вас у бою!" + fight: "У бій!" + watch_victory: "Подивитись Вашу перемогу" + defeat_the: "Перемогти" +# tournament_started: ", started" + tournament_ends: "Турнір завершуються" + tournament_ended: "Турнір завершено" + tournament_rules: "Правила турніру" + tournament_blurb: "Пиши код, збирай золото, будуй армії, розбивай ворогів, вигравай призи і покращуй свою кар'єру у нашому Greed Турнірі на 40 000 $! Дізнайся більше " + tournament_blurb_criss_cross: "Вигравай ставки, створюй шляхи, перехитри опонентів, збирай самоцвіти і покращуй свою кар'єру у нашому Criss-Cross Турнірі! Дізнайся більше " +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" + tournament_blurb_blog: "у нашому блозі" + rules: "Правила" + winners: "Переможці" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + user: + stats: "Статистика" + singleplayer_title: "Одиночні рівні" + multiplayer_title: "Мультиплеєрні рівні" + achievements_title: "Досягнення" + last_played: "Остання гра" + status: "Статус" + status_completed: "Завершено" + status_unfinished: "Незавершено" + no_singleplayer: "Жодної одиночної гри ще не зіграно." + no_multiplayer: "Жодної мультиплеєрної гри ще не зіграно." + no_achievements: "Жодних досягнень ще не отримано." + favorite_prefix: "Улюблена мова програмування " + favorite_postfix: "." + not_member_of_clans: "Досі не входить до жодного клану." -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + achievements: + last_earned: "Останні отримані" + amount_achieved: "Кількість" + achievement: "Досягнення" + category_contributor: "Співучасник" + category_ladder: "Драбина" + category_level: "Рівень" + category_miscellaneous: "Різне" + category_levels: "Рівні" + category_undefined: "без категорії" + current_xp_prefix: "" + current_xp_postfix: " загалом" + new_xp_prefix: "" + new_xp_postfix: " зароблено" + left_xp_prefix: "" + left_xp_infix: " до рівня " + left_xp_postfix: "" -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." + account: + recently_played: "Нещодавні ігри" + no_recent_games: "Упродовж останніх двох тижнів не зіграно жодної гри." + payments: "Платежі" + purchased: "Придбано" + subscription: "Підписка" + invoices: "Рахунки" + service_apple: "Apple" + service_web: "Веб" + paid_on: "Сплачено" + service: "Сервіс" + price: "Ціна" + gems: "Cамоцвіти" + active: "Активна" + subscribed: "Підписана" + unsubscribed: "Не підписана" + active_until: "Активна до" + cost: "Ціна" + next_payment: "Наступний платіж" + card: "Карта" + status_unsubscribed_active: "У Вас немає передплати і рахунок Вам не прийде, але Ваш акаунт і далі дійсний." + status_unsubscribed: "Отримайте доступ до новних рівнів, героїв та бонусів з абонементом CodeCombat!" -# loading_error: -# could_not_load: "Error loading from server" -# connection_failure: "Connection failed." -# unauthorized: "You need to be signed in. Do you have cookies disabled?" -# forbidden: "You do not have the permissions." -# not_found: "Not found." -# not_allowed: "Method not allowed." -# timeout: "Server timeout." -# conflict: "Resource conflict." -# bad_input: "Bad input." -# server_error: "Server error." -# unknown: "Unknown error." + account_invoices: + amount: "Сума у доларах США" + declined: "Вашу картку відхилено" + invalid_amount: "Будь ласка, введіть суму в доларах США." + not_logged_in: "Увійдіть або створіть акаунт, щоб мати доступ до інвойсів." + pay: "Оплатити інвойс" + purchasing: "Оплата..." + retrying: "Помилка сервера, повторна спроба." + success: "Успішно оплачено. Дякуємо!" -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" -# social_network_apis: "Social Network APIs" -# facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" -# facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" -# gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" -# user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" -# patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" -# thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" -# feedback: "Feedback" + loading_error: + could_not_load: "Помилка завантаження з сервера" + connection_failure: "Помилка з'єднання." + unauthorized: "Вам потрібно увійти. Ви вимкнули куки?" + forbidden: "Недостатньо прав." + not_found: "Не знайдено." + not_allowed: "Недозволений метод." + timeout: "Час очікування сервера минув." + conflict: "Конфлікт ресурсів." + bad_input: "Помилкове уведення." + server_error: "Помилка сервера." + unknown: "Невідома помилка." -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" + resources: + sessions: "Сесії" + your_sessions: "Ваші сесії" + level: "Рівень" + social_network_apis: "API соціальних мереж" + facebook_status: "Статус у Facebook" + facebook_friends: "Друзі з Facebook" + facebook_friend_sessions: "Сесії друзів з Facebook" + gplus_friends: "Друзі з G+" + gplus_friend_sessions: "Сесії друзів з G+" + leaderboard: "Таблиця лідерів" + user_schema: "Схема користувача" + user_profile: "Профайл користувача" + patch: "Патч" + patches: "Патчі" + patched_model: "Вихідний документ" + model: "Модель" + system: "Система" + systems: "Системи" + component: "Компонент" + components: "Компоненти" + thang: "Об'єкт" + thangs: "Об'єкти" + level_session: "Ваша сесія" + opponent_session: "Сесія опонента" + article: "Стаття" + user_names: "Імена користувачів" + thang_names: "Назви об'єктів" + files: "Файли" + top_simulators: "Топ-моделювальники" + source_document: "Вихідний документ" + document: "Документ" + sprite_sheet: "Лист спрайтів" + employers: "Роботодавці" + candidates: "Кандидатури" + candidate_sessions: "Сесії кандидатів" + user_remark: "Ремарка користувача" + user_remarks: "Ремарки користувачів" + versions: "Версії" + items: "Предмети" + hero: "Герой" + heroes: "Герої" + achievement: "Досягнення" + clas: "CLA" + play_counts: "Кількість ігор" + feedback: "Відгук" + payment_info: "Інформація про платіж" + campaigns: "Кампаніі" + poll: "Опитування" + user_polls_record: "Історія голосування в опитуваннях" -# guide: -# temp: "Temp" +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" + + delta: + added: "Додано" + modified: "Змінено" +# not_modified: "Not Modified" + deleted: "Вилучено" + moved_index: "Переміщено індекс" + text_diff: "Різниця тексту" + merge_conflict_with: "ОБ'ЄДНАТИ КОНФЛІКТ З" + no_changes: "Без змін" + + guide: + temp: "Тимч." multiplayer: multiplayer_title: "Налаштування мультиплеєра" # We'll be changing this around significantly soon. Until then, it's not important to translate. -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." - multiplayer_link_description: "Поділіться цим посиланням з будь-ким, щоб вони приєдналися до вас." + multiplayer_toggle: "Увімкнути мультиплеєр" + multiplayer_toggle_description: "Дозволити іншим приєднуватись до гри." + multiplayer_link_description: "Поділіться цим посиланням із будь-ким, щоб вони приєдналися до Вас." multiplayer_hint_label: "Підказка:" multiplayer_hint: "Натисніть на посилання, щоб обрати всіх, та натисніть Apple-C або Ctrl-C, щоб скопіювати посилання." - multiplayer_coming_soon: "Скоро - більше можливостей у мультиплеєрі!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." + multiplayer_coming_soon: "Незабаром - більше можливостей у мультиплеєрі!" + multiplayer_sign_in_leaderboard: "Увійдіть або створіть акаунт, аби помістити своє рішення до таблиці лідерів." legal: page_title: "Юридична інформація" opensource_intro: "CodeCombat - безкоштовна гра з повністю відкритим кодом." - opensource_description_prefix: "Завітайте" + opensource_description_prefix: "Завітайте " github_url: "на наш GitHub" opensource_description_center: "та долучайтесь, якщо хочете! CodeCombat побудовано на десятках проектів із вікритим кодом. і ми любимо їх. Перегляньте " - archmage_wiki_url: "нашу wiki для Архімагів," + archmage_wiki_url: "нашу вікі для Архімагів," opensource_description_suffix: "щоб побачити списки ПЗ, яке робить цю гру можливою." practices_title: "Шановні найкращі гравці" -# practices_description: "These are our promises to you, the player, in slightly less legalese." + practices_description: "Це наші обіцянки до тебе, гравцю, дещо менш юридичною мовою." privacy_title: "Конфіденційність" - privacy_description: "Ми не продаватимемо ніяку вашу особисту інформацію. Ми маємо намір заробити гроші за допомогою кінцевого результату, але будьте впевнені, ми не будемо поширювати Вашу особисту інформацію для зацікавлених компаній без вашої явної згоди." + privacy_description: "Ми не розповсюджуємо Вашу персональну інформацію." security_title: "Безпека" - security_description: "Ми прагнемо зберегти вашу особисту інформацію в безпеці. Як проект з відкритим кодом, наш сайт є вільно відкритим для всіх, щоб переглянути та удосконалити наші системи безпеки." + security_description: "Ми прагнемо зберегти Вашу особисту інформацію в безпеці. Як проект з відкритим кодом, наш сайт є вільно відкритим для всіх, щоб переглянути та удосконалити наші системи безпеки." email_title: "Email" - email_description_prefix: "Ми не будем завалювати вас спамом. Через" - email_settings_url: "налаштування вашого email" - email_description_suffix: "або через посилання в повідомленням котрі ми присилаємо, ви можете змінити ваші уподобання і легко відмовитись від підписки в будь-який час." + email_description_prefix: "Ми не будем завалювати Вас спамом. Через" + email_settings_url: "налаштування Вашого email" + email_description_suffix: "або через посилання в повідомленнях, котрі ми присилаємо, Ви можете змінити свої уподобання і легко відмовитись від підписки в будь-який час." cost_title: "Вартість" - cost_description: "На даний час, CodeCombat є безкоштовним на усі 100%! Однією з наших цілей є рухатись у цьому ж напрямку, так що у цю гру можуть грати стільки людей, наскільки це можливо, незалежно від місця проживання. Якщо настануть важкі часи, ми будем змушені стягувати плату за певний контент, але ми б не хотіли цього. Якщо пощастить, ми зможемо підтримувати компанію разом з:" - recruitment_title: "Доповнення" - recruitment_description_prefix: "Тут у CodeCombat, ви станете могутнім чарівником не лише у грі, але також і у реальному житті." - url_hire_programmers: "Ніхто не може найняти програмістів одразу" - recruitment_description_suffix: "отже як тільки ви заточите свої навики, і якщо ви погодитесь, ми продемонструєм ваші найкращі досягнення у кодуванні тисячам роботодавців котрі пускають слюні щоб не впустити можливості найняти вас. Вони платять нам не багато, вони платять вам" - recruitment_description_italic: "багато" - recruitment_description_ending: "сайт залишиться безкоштовним і кожен буде задоволеним. Такий план." + cost_description: "На даний час, CodeCombat є безкоштовним на усі 100%! Однією з наших цілей є рухатись у цьому ж напрямку, так що у цю гру можуть грати так багато людей, наскільки це можливо, незалежно від місця проживання. Якщо настануть важкі часи, ми будемо змушені стягувати плату за певний контент, але ми б не хотіли цього. Якщо пощастить, ми зможемо підтримувати компанію разом з:" copyrights_title: "Авторські права та ліцензії" contributor_title: "Авторська ліцензійна згода" contributor_description_prefix: "Усі права, як на сайті так і у нашому сховищі GitHub, є у відповідності з нашими" cla_url: "CLA" - contributor_description_suffix: "з котрими ви маєте погодитись перед співпрацею." + contributor_description_suffix: "з котрими Ви маєте погодитись перед співпрацею." code_title: "Код - MIT" - code_description_prefix: "Весь код що належить CodeCombat або розміщений на codecombat.com, як у сховищі GitHub так і у базі даних codecombat.com, є ліцензованим" + code_description_prefix: "Весь код, що належить CodeCombat або розміщений на codecombat.com, як у сховищі GitHub, так і у базі даних codecombat.com, є ліцензованим" mit_license_url: "ліцензією MIT" code_description_suffix: "Це включає весь код у системах та компонентах, котрі є доступними через CodeCombat з метою створення рівнів." art_title: "Мистецтво/Музика - творчі спільноти " art_description_prefix: "Увесь контент спільнот є доступним завдяки" cc_license_url: "міжнародній ліцензії Creative Commons із зазначенням авторства 4.0" - art_description_suffix: "Контентом спільнот є будь-що зроблене загальнодоступним через CodeCombat з метою створення рівнів. Це включає:" + art_description_suffix: "Контентом спільнот є будь-що, зроблене загальнодоступним через CodeCombat з метою створення рівнів. Це включає:" art_music: "Музику" art_sound: "Звук" art_artwork: "Творчі роботи" art_sprites: "Спрайти" art_other: "Будь-які і всі інші творчі роботи, котрі не є кодом, котрі є доступними для створення рівнів." - art_access: "На даний час немає універсальної, зручної системи для вибору цих активів. В загальному, вибирайте їх з URL котрі використовуються сайтом, зверніться до нас за допомогою, або допоможіть нам в розширенні цього сайту, щоб зробити ці активи більш доступними." - art_paragraph_1: "Щодо авторських прав, будь-ласка, назвіть і дайте лінк на codecombat.com де саме ресурс використовується або де призначатиметься у середовищі. Для прикладу:" - use_list_1: "Якщо використовується в ролику або іншій грі, додайте codecombat.com в the credits." - use_list_2: "Якщо використовується на веб-сайті, додайте лінк біля статистики, на приклад під зображенням, або в загальному описі сторінки де ви зможете також згадати інші роботи Creative Commons і програмне забезпечення з відкритим кодом, що використовується на сайті. Дещо, що вже має чисті посилання на CodeCombat, так як повідомлення у блозі у котрих згадується CodeCombat, не потребують окремих посилань." + art_access: "На даний час немає універсальної, зручної системи для вибору цих активів. В загальному, вибирайте їх з URL, котрі використовуються сайтом, зверніться до нас за допомогою або допоможіть нам в розширенні цього сайту, щоб зробити ці активи більш доступними." + art_paragraph_1: "Щодо авторських прав, будь-ласка, назвіть і дайте лінк на codecombat.com, де саме ресурс використовується або де призначатиметься у середовищі. Для прикладу:" + use_list_1: "Якщо використовується в ролику або іншій грі, додайте codecombat.com у 'credits'." + use_list_2: "Якщо використовується на веб-сайті, додайте лінк біля статистики, на приклад під зображенням, або в загальному описі сторінки, де ви зможете також згадати інші роботи Creative Commons і програмне забезпечення з відкритим кодом, що використовується на сайті. Дещо, що вже має чисті посилання на CodeCombat, такі як повідомлення у блозі, у котрих згадується CodeCombat, не потребують окремих посилань." art_paragraph_2: "Якщо використаний контент не був створений CodeCombat але внесений користувачем codecombat.com, роблячи посилання на нього, дотримуйтесь інструкції посилання передбачених в описі цього ресурсу, якщо такі інструкції є." rights_title: "Права захищені" rights_desc: "Усі права захищені для самих рівнів. Це включає" @@ -922,173 +1280,157 @@ module.exports = nativeDescription: "українська мова", englishDesc rights_unit: "Конфігурації блоку" rights_description: "Опис" rights_writings: "Листи" - rights_media: "Медіа (звуки, музику) і будь-який інший творчий контент зроблений конкретно для того рівня і не зроблений загальнодостурним для створенні рівнів." - rights_clarification: "Для уточнення, будь-що що є доступним у редакторі рівнів, для створення нових рівнів, належить CC, тоді як контент створений редактором рівнів або завантажений в ході створення рівнів, не належить." + rights_media: "Медіа (звуки, музику) і будь-який інший творчий контент, зроблений конкретно для того рівня і не зроблений загальнодоступним для створення рівнів." + rights_clarification: "Для уточнення, будь-що що є доступним у редакторі рівнів, для створення нових рівнів, належить CC, тоді як контент, створений редактором рівнів або завантажений у ході створення рівнів, не належить." nutshell_title: "Коротко" - nutshell_description: "Будь-які ресурси котрі ми надаємо в редакторі рівнів є безкоштовними для використання за вашим бажанням для створення рівнів. Але ми залишаємо за собою право обмежувати розповсюдження самих рівнів(котрі були створені на codecombat.com), тому вони зможуть заплатити в майбутньому, якщо в кінцевому результаті таке станеться." - canonical: "Англійська версія цього документа є остаточною та канонічною версією. Якщо тут є будь-які невідповідності в перекладі, англійська версія документа є пріоритетною." + nutshell_description: "Будь-які ресурси, котрі ми надаємо в редакторі рівнів, є безкоштовними для використання за Вашим бажанням для створення рівнів. Але ми залишаємо за собою право обмежувати розповсюдження самих рівнів (котрі були створені на codecombat.com), тому в майбутньому за них може стягуватися плата, якщо в кінцевому результаті таке станеться." + canonical: "Англомовна версія цього документа є остаточною та канонічною версією. Якщо є будь-які невідповідності в перекладі, англійська версія документа є пріоритетною." -# ladder_prizes: -# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - - wizard_settings: - title: "Налаштування" - customize_avatar: "Налаштувати аватар" - active: "Діючий" - color: "Колір" - group: "Група" - clothes: "Одяг" - trim: "Оздоблення" - cloud: "Хмаринка" - team: "Команда" - spell: "Закляття" - boots: "Черевики" - hue: "Відтінок" - saturation: "Насиченість" - lightness: "Яскравість" + ladder_prizes: + title: "Нагороди за турнір" # This section was for an old tournament and doesn't need new translations now. + blurb_1: "Цими призами буде нагороджено залежно від" + blurb_2: "правил турніру" + blurb_3: "першості серед людей або оґрів." + blurb_4: "Дві команди – подвоєння призів!" + blurb_5: "(Два переможня на першому місці, два на другому, тощо.)" + rank: "Ранг" + prizes: "Призи" + total_value: "Загалом" + in_cash: "Готівкою" + custom_wizard: "Власний чарівник CodeCombat" + custom_avatar: "Власний аватар CodeCombat" + heap: "шість місяців доступу \"Startup\"" + credits: "Кредити" + one_month_coupon: "купон: оберіть Rails або HTML" + one_month_discount: "знижка 30%: оберіть Rails або HTML" + license: "ліцензія" + oreilly: "електронна книга на ваш вибір" account_profile: -# settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" + settings: "Налаштування" # We are not actively recruiting right now, so there's no need to add new translations for this section. + edit_profile: "Редагувати профіль" + done_editing: "Завершити редагування" profile_for_prefix: "Профіль для " profile_for_suffix: "" -# featured: "Featured" -# not_featured: "Not Featured" + featured: "Включає" + not_featured: "Не включає" looking_for: "Шукає:" last_updated: "Останнє оновлення:" contact: "Сконтактуватись" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." -# next_name: "name?" -# next_short_description: "write a short description." -# next_long_description: "describe your desired position." -# next_skills: "list at least five skills." -# next_work: "chronicle your work history." -# next_education: "recount your educational ordeals." -# next_projects: "show off up to three projects you've worked on." -# next_links: "add any personal or social links." -# next_photo: "add an optional professional photo." -# next_active: "mark yourself open to offers to show up in searches." -# example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" -# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" -# links_name_help: "What are you linking to?" -# links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" -# basics_city: "City" -# basics_city_help: "City you want to work in (or live in now)." -# basics_country: "Country" -# basics_country_help: "Country you want to work in (or live in now)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" -# basics_looking_for_full_time: "Full-time" -# basics_looking_for_part_time: "Part-time" -# basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." + active: "Шукаю пропозиції роботи в даний час" + inactive: "Не шукаю пропозицій роботи в даний час" + complete: "Готово" + next: "Далі" + next_city: "місто?" + next_country: "виберіть вашу країну." + next_name: "ім'я?" + next_short_description: "напишіть короткий опис." + next_long_description: "опишіть бажану позицію." + next_skills: "опишіть щонайменше п'ять своїх навичок." + next_work: "опишіть ваш досвід роботи." + next_education: "розкажіть про вашу освіту." + next_projects: "опишіть нам три проекта над якими ви працювали." + next_links: "додайте посилання на особисту сторінку або соц. мережі." + next_photo: "додайте необов’язкове професійне фото." + next_active: "відзначте що Ви у пошуках пропозицій, щобвідображатися у пошуку." + example_blog: "Блог" + example_personal_site: "Особиста Сторінка" + links_header: "Особисті Посилання" + links_blurb: "Посилання на інші сторінки або профілі, які б ви хотіли вказати. Наприклад: аккаунт на GitHub'і, LinkedIn, або ваш блог. " + links_name: "Назва посилання" + links_name_help: "На що Ви посилаєтесь?" + links_link_blurb: "URL посилання" + basics_header: "Поновити базову інформацію" + basics_active: "У пошуках пропозицій" + basics_active_help: "Шукати пропозиціі прямо зараз?" + basics_job_title: "Бажана позиція у роботі" + basics_job_title_help: "Яку роль Ви шукаєте?" + basics_city: "Місто" + basics_city_help: "Місто, в якому б Ви хотіли працювати (або зараз проживаєте)." + basics_country: "Країна" + basics_country_help: "Країна, в якій Ви хотіли б працювати (або зараз живете)." + basics_visa: "Робочий статус у США" + basics_visa_help: "У Вас є право на роботу у США чи Вам потрібна допомога з отриманням візи? (Якщо проживаєте у Канаді чи Австралії, вкажіть, як перше.)" + basics_looking_for: "Шукаю" + basics_looking_for_full_time: "Повна" + basics_looking_for_part_time: "Часткова" + basics_looking_for_remote: "Віддалена" + basics_looking_for_contracting: "За контрактом" + basics_looking_for_internship: "Стажування" + basics_looking_for_help: "Яку посаду розробника Ви б хотіли?" + name_header: "Введіть своє ім'я" + name_anonymous: "Анонімний розробник" + name_help: "Ім'я, яке Ви хочете повідомити своїм роботодавців, напр., 'Nick Winter'." + short_description_header: "Коротко розкажіть про себе" + short_description_blurb: "Додайте слоган, за яким роботодавець швидко складе уявлення про Вас." + short_description: "Слоган" + short_description_help: "Хто Ви і чого шукаєте? Макс. 140 символів." + skills_header: "Навички" + skills_help: "Вкажіть відповідні розробницькі навички за досвідченістю." + long_description_header: "Опишіть бажану посаду" + long_description_blurb: "Розкажіть роботодавцям, які Ви чудові і яку роль для себе хочете." + long_description: "Представлення" + long_description_help: "Опишіть себе потенційним роботодавцям. Коротко і по суті. Рекомендуємо підкреслити позицію, яка найбільше Вас цікавить. Допустима доречна розмітка, макс. 600 символів." work_experience: "Досвід роботи" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" + work_header: "Вкажіть хроніку своєї історії роботи" + work_years: "Роки досвіду" + work_years_help: "Скільки у Вас років досвіду професійної розробки програмного забезпечення (з отриманням платні)?" + work_blurb: "Список відповідного досвіду роботи, від най свіжішого." + work_employer: "Роботодавець" + work_employer_help: "Ім'я Вашого роботодавця." + work_role: "Назва посади" + work_role_help: "Як називалась Ваша посада чи роль?" + work_duration: "Тривалість" # work_duration_help: "When did you hold this gig?" -# work_description: "Description" -# work_description_help: "What did you do there? (140 chars; optional)" + work_description: "Опис" + work_description_help: "Що Ви там робили? (140 символів; необов'язково)" education: "Освіта" # education_header: "Recount your academic ordeals" # education_blurb: "List your academic ordeals." -# education_school: "School" + education_school: "Школа" # education_school_help: "Name of your school." -# education_degree: "Degree" + education_degree: "Ступінь" # education_degree_help: "What was your degree and field of study?" # education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" + education_duration_help: "Коли?" + education_description: "Опис" # education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" our_notes: "Наші примітки" -# remarks: "Remarks" + remarks: "Примітки" projects: "Роботи" # projects_header: "Add 3 projects" # projects_header_2: "Projects (Top 3)" # projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" + project_name: "Назва проекту" # project_name_help: "What was the project called?" -# project_description: "Description" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" + project_description: "Опис" + project_description_help: "Коротко опишіть проект." + project_picture: "Зображення" # project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" -# project_link_help: "Link to the project." + project_link: "Посилання" + project_link_help: "Посилання на проект." # player_code: "Player Code" employers: -# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." -# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." -# hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. + deprecation_warning_title: "Вибачте, зараз CodeCombat не пропонує роботу." + deprecation_warning: "Наразі ми зосередилися на рівнях для новачків замість пошуків розробників-експертів." + hire_developers_not_credentials: "Наймаємо розробників, а не рекомендаційні листи." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. # get_started: "Get Started" # already_screened: "We've already technically screened all our candidates" # filter_further: ", but you can also filter further:" -# filter_visa: "Visa" + filter_visa: "Visa" # filter_visa_yes: "US Authorized" # filter_visa_no: "Not Authorized" # filter_education_top: "Top School" # filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" + filter_role_web_developer: "веб-розробник" + filter_role_software_developer: "розробник програм" + filter_role_mobile_developer: "мобільний розробник" + filter_experience: "Досвід" # filter_experience_senior: "Senior" # filter_experience_junior: "Junior" # filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" + filter_experience_student: "Студент коледжу" # filter_results: "results" # start_hiring: "Start hiring." # reasons: "Three reasons you should hire through us:" @@ -1103,30 +1445,30 @@ module.exports = nativeDescription: "українська мова", englishDesc # what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." # cost: "How much do we charge?" # cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." - candidate_name: "Ім‘я" + candidate_name: "Ім'я" candidate_location: "Розташування" candidate_looking_for: "Шукає" candidate_role: "Роль" candidate_top_skills: "Найкращі навички" candidate_years_experience: "років досвіду" candidate_last_updated: "Останнє оновлення" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" + candidate_who: "Хто" + featured_developers: "Рекомендовані розробники" + other_developers: "Інші розробники" + inactive_developers: "Неактивні розробники" admin: # av_espionage: "Espionage" # Really not important to translate /admin controls. -# av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" + av_espionage_placeholder: "Користвач або email" + av_usersearch: "Пошук користувачів" # av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" + av_usersearch_search: "Пошук" av_title: "Види для адміна" av_entities_sub_title: "Організації" av_entities_users_url: "Учасники" av_entities_active_instances_url: "Активні вимоги" -# av_entities_employer_list_url: "Employer List" -# av_entities_candidates_list_url: "Candidate List" + av_entities_employer_list_url: "Список робітників" + av_entities_candidates_list_url: "Список кандидатів" # av_entities_user_code_problems_list_url: "User Code Problems List" av_other_sub_title: "Інші" av_other_debug_base_url: "Основне (для налагодження base.jade)" diff --git a/app/locale/ur.coffee b/app/locale/ur.coffee index aca26620b..65b260fad 100644 --- a/app/locale/ur.coffee +++ b/app/locale/ur.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # slogan: "Learn to Code by Playing a Game" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level +# play: "Play" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" # login: # sign_up: "Create Account" # log_in: "Log In" # logging_in: "Logging In" # log_out: "Log Out" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "Loading..." # saving: "Saving..." # sending: "Sending..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # fork: "Fork" # play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/en-AU.coffee b/app/locale/uz.coffee similarity index 69% rename from app/locale/en-AU.coffee rename to app/locale/uz.coffee index 9550bac2b..482824ac7 100644 --- a/app/locale/en-AU.coffee +++ b/app/locale/uz.coffee @@ -1,11 +1,12 @@ -module.exports = nativeDescription: "English (AU)", englishDescription: "English (AU)", translation: +module.exports = nativeDescription: "O'zbekcha", englishDescription: "Uzbek", translation: # home: # slogan: "Learn to Code by Playing a Game" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level +# play: "Play" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -38,11 +39,11 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # not_found: # page_not_found: "Page not found" -# diplomat_suggestion: + diplomat_suggestion: # title: "Help translate CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. # sub_heading: "We need your language skills." -# pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in {English} but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into {English}." -# missing_translations: "Until we can translate everything into {English}, you'll see English when {English} isn't available." + pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Uzbek but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Uzbek." + missing_translations: "Until we can translate everything into Uzbek, you'll see English when Uzbek isn't available." # learn_more: "Learn more about being a Diplomat" # subscribe_as_diplomat: "Subscribe as a Diplomat" @@ -56,62 +57,60 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" # login: # sign_up: "Create Account" # log_in: "Log In" # logging_in: "Logging In" # log_out: "Log Out" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -126,8 +125,10 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # misc: "Misc" # books: "Books" - common: - loading: "Loading..." +# common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" +# loading: "Loading..." # saving: "Saving..." # sending: "Sending..." # send: "Send" @@ -139,9 +140,14 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # fork: "Fork" # play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/vi.coffee b/app/locale/vi.coffee index abf72134d..74c9d4835 100644 --- a/app/locale/vi.coffee +++ b/app/locale/vi.coffee @@ -1,35 +1,36 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietnamese", translation: home: - slogan: "Học mã bằng chơi Games" + slogan: "Học code bằng chơi Games" no_ie: "Codecombat không chạy trong Internet Explorer 8 hoặc cũ hơn. Xin lỗi!" # Warning that only shows up in IE8 and older no_mobile: "Codecombat không được thiết kế cho các thiết bị di động và có thể không hoạt động được!" # Warning that shows up on mobile devices - play: "Chơi" # The big play button that just starts playing a level -# old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari -# old_browser_suffix: "You can try anyway, but it probably won't work." -# campaign: "Campaign" + play: "Chơi" # The big play button that opens up the campaign view. + old_browser: "Trình duyệt của bạn quá cũ để chạy CodeCombat. Thật sự xin lỗi!" # Warning that shows up on really old Firefox/Chrome/Safari + old_browser_suffix: "Bạn có thể thử nếu bạn muốn, nhưng tôi nghĩ nó sẽ không hoạt động." + ipad_browser: "Tin xấu: CodeCombat không chạy trên trình duyệt web của iPad. Tin tốt: Ứng dụng dành cho iPad của chúng tôi đang chờ sự chấp thuận của Apple." + campaign: "Chiến dịch" for_beginners: "Dành cho người bắt đầu chơi" -# multiplayer: "Multiplayer" # Not currently shown on home page -# for_developers: "For Developers" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + multiplayer: "Nhiều người chơi" # Not currently shown on home page + for_developers: "Dành cho nhà phát triển" # Not currently shown on home page. + or_ipad: "Hoặc tải phiên bản dành cho iPad" nav: - play: "Các cấp độ" # The top nav bar entry where players choose which levels to play -# community: "Community" + play: "Chơi" # The top nav bar entry where players choose which levels to play + community: "Cộng đồng" editor: "Chỉnh sửa" -# blog: "Blog" - forum: "Diễn đàn" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + blog: "Blog" + forum: "Diễn đàn" + account: "Tài khoản" + profile: "Thông tin cá nhân" + stats: "Các chỉ số" + code: "Code" admin: "Quản trị viên" # Only shows up when you are an admin home: "Nhà" - contribute: "Contribute" - legal: "Hợp pháp" - about: "Về" - contact: "Liên hệ" - twitter_follow: "Đi theo" -# teachers: "Teachers" + contribute: "Đóng góp" + legal: "Hợp pháp" + about: "Giới thiệu" + contact: "Liên hệ" + twitter_follow: "Theo dõi" + teachers: "Dành cho giáo viên" modal: close: "Đóng" @@ -41,327 +42,478 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn diplomat_suggestion: title: "Hãy giúp dịch thuật cho CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "Chúng tôi cần kỹ năng ngoại ngữ của bạn." - pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Vietnamese but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Vietnamese." - missing_translations: "Until we can translate everything into Vietnamese, you'll see English when Vietnamese isn't available." -# learn_more: "Learn more about being a Diplomat" -# subscribe_as_diplomat: "Subscribe as a Diplomat" + pitch_body: "Chúng tôi ban đầu thiết kế Codecombat dựa trên ngôn ngữ Tiếng Anh, tuy nhiên có rất nhiều bạn trẻ trên toàn thế giới đã tham gia vào trò chơi này. Rất nhiều các bạn rất muốn chơi và có giao diện Tiếng Việt, vì vậy nếu như bạn có thể đọc và viết cả 2 ngôn ngữ xin hãy đăng kí làm thông dịch viên cho chúng tôi." + missing_translations: "Bạn sẽ tiếp tục thấy tiếng Anh cho đến khi chúng tôi kiếm được người dịch tất cả những từ này qua Tiếng Việt." + learn_more: "Để biết thêm chi tiết làm thông dịch viên" + subscribe_as_diplomat: "Tham gia phiên dịch" play: -# play_as: "Play As" # Ladder page -# spectate: "Spectate" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" + play_as: "Chơi là" # Ladder page + spectate: "Quan sát" # Ladder page + players: "Những người chơi" # Hover over a level on /play + hours_played: "Thời gian chơi" # Hover over a level on /play + items: "Trang bị" # Tooltip on item shop button from /play + unlock: "Mua" # For purchasing items and heroes + confirm: "Xác nhận" + owned: "Đã có" # For items you own + locked: "Đã khóa" + purchasable: "Có thể mua" # For a hero you unlocked but haven't purchased + available: "Có thể dùng" # skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + heroes: "Những nhân vật" # Tooltip on hero shop button from /play + achievements: "Những thành tích" # Tooltip on achievement list button from /play + account: "Tài khoản" # Tooltip on account button from /play + settings: "Những tùy chỉnh" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "Tiếp" # Go from choose hero to choose inventory before playing a level + change_hero: "Thay đổi nhân vật" # Go back from choose inventory to choose hero + choose_inventory: "Đeo Thiết Bị" + buy_gems: "Mua ngọc" + subscription_required: "Cần đăng kí" + anonymous: "Người chơi vô danh" level_difficulty: "Khó: " - campaign_beginner: "Bắt đầu chiến dịch" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "Chọn Trình của bạn" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "Bạn có thể nhảy đến bất kỳ cấp độ dưới đây, hoặc nâng dần cấp độ " - adventurer_forum: "diễn đàn Adventurer" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." - campaign_dev: "Các cấp độ khó hơn ngẫu nhiên" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." + campaign_beginner: "Chiến dịch chế độ đơn giản" + awaiting_levels_adventurer_prefix: "Chúng tôi cho ra 5 bàn mới mỗi tuần." # {change} + awaiting_levels_adventurer: "Đăng kí với tư cách là nhà thám hiểm" + awaiting_levels_adventurer_suffix: "để trở thành những người đầu tiên chơi những bàn mới." + adjust_volume: "Tùy chỉnh âm lượng" campaign_multiplayer: "Khu vực đa người chơi" -# campaign_multiplayer_description: "... in which you code head-to-head against other players." - campaign_player_created: "Tạo người chơi" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" + campaign_multiplayer_description: "... nơi mà bạn thử thách với một người chơi khác." +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "Bạn đang tiến bộ rõ rệt đấy! Hãy kể cho ai đó xem bạn đã học được nhiều chừng nào với CodeCombat." + email_invalid: "Địa chỉ email không hợp lệ." + form_blurb: "Nhập địa chỉ email của người bạn muốn tìm và chúng tôi sẽ cho bạn thấy!" + form_label: "Địa chỉ email" + placeholder: "địa chỉ email" + title: "Làm tốt lắm, thực tập viên" login: - sign_up: "Tạo tài khoản" - log_in: "Đăng nhập" -# logging_in: "Logging In" + sign_up: "Tạo tài khoản" + log_in: "Đăng nhập" + logging_in: "Đang đăng nhập" log_out: "Đăng xuất" - recover: "Khôi phục tài khoản" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "Quên mật khẩu?" + authenticate_gplus: "Cho phép G+" + load_profile: "Đọc tài khoản G+" + finishing: "Sắp hoàn tất" + sign_in_with_facebook: "Đăng nhập với Facebook" + sign_in_with_gplus: "Đăng nhập với G+" + signup_switch: "Bạn có muốn tạo tài khoản mới?" signup: - create_account_title: "Tạo tài khoản để lưu tiến trình" - description: "Nó miễn phí. Chỉ cần một vài thứ và bạn có thể tiếp tục:" email_announcements: "Nhận thông báo bằng email" - coppa: "13+ hoặc non-USA " - coppa_why: "(Tại sao?)" creating: "Tạo tài khoản..." sign_up: "Đăng ký" log_in: "đăng nhập với mật khẩu" -# social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." + social_signup: "Hoặc bạn có thể đăng kí qua Facebook hoặc G+:" + required: "Bạn cần phải đăng nhập trước khi đi bạn có thể đi hướng đó." + login_switch: "Bạn đã có tài khoản rồi ?" recover: recover_account_title: "Khôi phục tài khoản" send_password: "Gởi mật mã khôi phục" -# recovery_sent: "Recovery email sent." + recovery_sent: "Đã gửi email giúp bạn khôi phục mật khẩu." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Chính" + secondary: "Phụ" + armor: "Áo giáp" + accessories: "Phụ kiện" + misc: "Linh tinh" + books: "Cuốn sách" common: + back: "Quay trở lại" # When used as an action verb, like "Navigate backward" + continue: "Tiếp tục" # When used as an action verb, like "Continue forward" loading: "Tải..." saving: "Lưu..." sending: "Gởi..." -# send: "Send" + send: "Gởi đi" cancel: "Hủy" save: "Lưu" -# publish: "Publish" -# create: "Create" -# manual: "Manual" -# fork: "Fork" + publish: "Chia sẻ" + create: "Làm mới" + manual: "Cẩm nang" + fork: "Nĩa" play: "Các cấp độ" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + retry: "Chơi lại" + actions: "Các Hành Động" + info: "Thông tin" + help: "Giúp đỡ" + watch: "Quan Sát" + unwatch: "Ngừng Quan Sát" + submit_patch: "Gửi Bản Cập Nhật" + submit_changes: "Gửi Những Thay Đổi" +# save_changes: "Save Changes" -# general: -# and: "and" -# name: "Name" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" -# or: "or" -# subject: "Subject" -# email: "Email" -# password: "Password" -# message: "Message" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + general: + and: "và" + name: "Tên" + date: "Ngày" + body: "Thân" + version: "Phiên bản" + pending: "Trong quá trình quyết định" + accepted: "Đã được chấp nhận" + rejected: "Không được chấp nhận" + withdrawn: "Thu Hồi" + submitter: "Người gửi" + submitted: "Đã gửi" + commit_msg: "Tin nhắn sự thay đổi" + review: "Kiểm Tra" + version_history: "Các phiên bản trong quá khứ" + version_history_for: "Các phiên bản trong quá khứ cho: " + select_changes: "Chọn 2 sự thay đổi dưới đây để thấy sự khác biệt." + undo_prefix: "Hủy bỏ" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "Làm lại" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "Xem trước cách chơi cho màn này" + result: "Kết quả" + results: "Những kết quả" + description: "Mô tả" + or: "hay là" + subject: "Chủ đề" + email: "Email" + password: "Mật khẩu" + message: "Tin nhắn" + code: "Code" + ladder: "Thang Điểm" + when: "Khi nào" + opponent: "Đối thủ" + rank: "Hạng" + score: "Điểm" + win: "Thắng" + loss: "Thua" + tie: "Hòa" + easy: "Dễ̉" + medium: "Vừa" + hard: "Khó" + player: "Người chơi" + player_level: "Cấp" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "Chiến binh" + ranger: "Cung thủ" + wizard: "Phù thủy" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "giây" + seconds: "giây" + minute: "phút" + minutes: "phút" + hour: "giờ" + hours: "giờ" + day: "ngày" + days: "ngày" + week: "tuần" + weeks: "tuần" + month: "tháng" + months: "tháng" + year: "năm" + years: "năm" play_level: done: "Hoàn thành" # home: "Home" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" + level: "Bàn" # Like "Level: Dungeons of Kithgard" + skip: "Bỏ qua" # game_menu: "Game Menu" guide: "Hướng dẫn" restart: "Khởi động lại" - goals: "Mục đích" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" + goals: "Mục tiêu" + goal: "Mục tiêu" + running: "Đang chạy..." + success: "Thắng!" + incomplete: "Chưa hoàn thành" + timed_out: "Hết giờ" + failing: "Đang thua" # action_timeline: "Action Timeline" - click_to_select: "Kích vào đơn vị để chọn nó." -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + click_to_select: "Kích vào mục tiêu để chọn nó." + control_bar_multiplayer: "Nhiều người chơi" + control_bar_join_game: "Tham gia" + reload: "Tải lại" reload_title: "Tải lại tất cả mã?" -# reload_really: "Are you sure you want to reload this level back to the beginning?" -# reload_confirm: "Reload All" -# victory_title_prefix: "" -# victory_title_suffix: " Complete" -# victory_sign_up: "Sign Up to Save Progress" -# victory_sign_up_poke: "Want to save your code? Create a free account!" -# victory_rate_the_level: "Rate the level: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" -# victory_go_home: "Go Home" # Only in old-style levels. -# victory_review: "Tell us more!" # Only in old-style levels. -# victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" -# guide_title: "Guide" -# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. + reload_really: "Bạn có chắc bạn muốn tải lại bàn này về lúc ban đầu?" + reload_confirm: "Tải lại tất cả" + victory: "Thắng" + victory_title_prefix: "" + victory_title_suffix: " Xong" + victory_sign_up: "Đăng kí để lưu tiến trình của bạn" + victory_sign_up_poke: "Bạn có muốn lưu code của mình lại không ? Hãy tạo một tài khoản miễn phí!" + victory_rate_the_level: "Đánh giá màn chơi: " # Only in old-style levels. + victory_return_to_ladder: "Quay lại bảng điểm" + victory_play_continue: "Tiếp tục" + victory_saving_progress: "Đang lưu tiến trình" + victory_go_home: "Quay về màn hình chính" # Only in old-style levels. + victory_review: "Hãy cho chúng tôi biết thêm" # Only in old-style levels. + victory_hour_of_code_done: "Bạn xong chưa?" + victory_hour_of_code_done_yes: "Đúng vậy, tôi đã hoàn tất thời gian lập trình!" + victory_experience_gained: "XP nhận được" + victory_gems_gained: "Ngọc nhận được" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" + guide_title: "Hướng dẫn" + tome_minion_spells: "Phép của lính của bạn" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. -# tome_other_units: "Other Units" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" + tome_other_units: "Các thứ khác" # Only in old-style levels. + tome_cast_button_run: "Chạy" + tome_cast_button_running: "Đang chạy" + tome_cast_button_ran: "Đã chạy" + tome_submit_button: "Gửi" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). -# tome_select_a_thang: "Select Someone for " -# tome_available_spells: "Available Spells" -# tome_your_skills: "Your Skills" +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). + tome_select_a_thang: "Chọn một người để " + tome_available_spells: "Phép có thể dùng" + tome_your_skills: "Những kĩ năng của bạn" + tome_help: "Giúp đỡ" # tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" + hud_continue_short: "Tiếp tục" + code_saved: "Code đã được lưu" + skip_tutorial: "Bỏ qua (esc)" + keyboard_shortcuts: "Các phím tắt" + loading_ready: "Sẵn sàng!" + loading_start: "Bắt đầu bàn này" + problem_alert_title: "Hãy sửa code của bạn" + problem_alert_help: "Giúp đỡ" + time_current: "Bây giờ:" + time_total: "Tối đa: " + time_goto: "Đi đến:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "Chơi Lại" + infinite_loop_reset_level: "Chơi Lại" # infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." + tip_toggle_play: "Bật/tắt chơi/dừng với Ctrl+P." + tip_scrub_shortcut: "Ctrl+[ và Ctrl+] quay lại và chạy nhanh." # {change} + tip_guide_exists: "Chọn bảng hướng dẫn, bên trong game menu (Trên cùng của trang), cho những thông tin hữu ích." + tip_open_source: "CodeCombat là một sản phẩm mã nguồn mở 100% !" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "Bản thử nghiệm của CodeCombat được ra mắt vào tháng 10 năm 2013." + tip_think_solution: "Nghĩ về cách giải, chứ không phải câu hỏi." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" + tip_error_free: "Có 2 cách để thiết kế 1 phần mềm ko có lỗi; tuy nhiên chỉ có cách thứ 3 là đúng. - Alan Perlis" # tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." + tip_forums: "Hãy đi qua diễn đàn và kể cho chúng tôi nghe về những suy nghĩ của bạn!" + tip_baby_coders: "Trong tương lai, ngay cả em bé cũng sẽ trở thành 'Archmages'" # tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." + tip_all_species: "Chúng tôi tin rằng mọi loài đều xứng đáng có được cơ hội để học lập trình." # tip_reticulating: "Reticulating spines." # tip_harry: "Yer a Wizard, " # tip_great_responsibility: "With great coding skill comes great debug responsibility." # tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." + tip_binary: "Có 10 loại người trên thế giới này: những người biết về hệ nhị phân và những người không biết về nó." # tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" + tip_no_try: "Làm. Hoặc không làm. Không có chuyện thử. - Yoda" # tip_patience: "Patience you must have, young Padawan. - Yoda" # tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" + tip_impossible: "Sự việc đều tưởng như bất khả thi cho tới khi nó được hoàn thành. - Nelson Mandela" # tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" # tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." + tip_hardware_problem: "Câu hỏi: Cần bao nhiêu lập trình viên để thay 1 bóng đèn? Trá lời: 0 người. Đó là vấn đề về phần cứng." # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Tùy chỉnh Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" + tip_move_forward: "Dù bạn làm gì đi chăng nữa thì hãy tiến lên và đừng bao giờ bỏ cuộc. - Martin Luther King Jr." + tip_google: "Có vấn đề mà bạn không thể giải quyết ? Hãy sử dụng Google để tìm ra phương án!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" - multiplayer_tab: "Nhiều người chơi" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" + inventory_tab: "Hành Trang" + save_load_tab: "Lưu/đọc" + options_tab: "Những lựa chọn" + guide_tab: "Bảng hướng dẫn" + guide_video_tutorial: "Video hướng dẫn" + guide_tips: "Một số mẹo" + multiplayer_tab: "Đa người chơi" + auth_tab: "Đăng kí" + inventory_caption: "Trang bị cho nhân vật của bạn" + choose_hero_caption: "Chọn nhân vật, ngôn ngữ" # save_load_caption: "... and view history" # options_caption: "Configure settings" # guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + multiplayer_caption: "Chơi với những người bạn!" + auth_caption: "Lưu tiến trình của bạn." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" + leaderboard: + leaderboard: "Bảng xếp hạng" + view_other_solutions: "Xem bài giải của những người khác" # {change} + scores: "Điểm" +# top_players: "Top Players by" + day: "Hôm nay" + week: "Tuần này" + all: "Mọi thời đại" + time: "Thời gian" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" + difficulty: "Độ khó" + gold_collected: "Lượng vàng đã thu thập" + + inventory: + choose_inventory: "Các Thiết Bị Đang Mặc" + equipped_item: "Đã trang bị" + required_purchase_title: "Yêu cầu" # available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + restricted_title: "Bị giới hạn" + should_equip: "(nhấn đúp chuột để trang bị)" + equipped: "(đã trang bị)" + locked: "(đã khóa)" + restricted: "(bị giới hạn ở bàn này)" + equip: "Trang bị" + unequip: "Không trang bị nữa" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + buy_gems: + few_gems: "Một vài viên ngọc" + pile_gems: "Một chồng ngọc" + chest_gems: "Một hòm ngọc" + purchasing: "Đang mua..." + declined: "Thẻ của bạn không được chấp nhận" + retrying: "Lỗi server, đang thử lại." + prompt_title: "Không đủ ngọc" + prompt_body: "Ban có muốn nhiều ngọc hơn không ?" + prompt_button: "Vào cửa hàng" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." + subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" + feature2: "7 <strong>nhât vật mới</strong> mạnh mẽ với những kĩ năng đặc biệt!" # {change} +# feature3: "70+ bonus levels" + feature4: "<strong>Được thưởng thêm 3500 ngọc</strong> mỗi tháng!" + feature5: "Những video hướng dẫn qua bàn" + feature6: "Sự hỗ trợ tận tình qua email" +# feature7: "Private <strong>Clans</strong>" + free: "Miễn phí" + month: "tháng" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" + never_mind: "Đừng bận tâm, chúng tôi vẫn yêu bạn" + thank_you_months_prefix: "Cảm ơn bạn đã ủng hộ chúng tôi trong những" + thank_you_months_suffix: "tháng vừa qua." + thank_you: "Cảm ơn bạn đã ủng hộ CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." + unsubscribe_feedback_placeholder: "Ồ, chúng tôi đã làm gì sai ư?" + parent_button: "Hãy hỏi bố mẹ bạn" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." + parent_email_input_invalid: "Địa chỉ email không hợp lệ." + parent_email_input_label: "Địa chỉ email của bố hoặc mẹ bạn" + parent_email_input_placeholder: "Hãy nhập địa chi email của bố hoặc mẹ bạn" + parent_email_send: "Gửi Email" + parent_email_sent: "Đã gửi Email!" + parent_email_title: "Địa chỉ email của bố hoặc mẹ bạn là gì ?" + parents: "Dành cho cha mẹ" + parents_title: "Con của bạn sẽ học cách viết code (lập trình)." # {change} + parents_blurb1: "Với CodeCombat, con của bạn sẽ có thể học lập trình bằng việc viết những dòng code thật sự. Con bạn sẽ bắt đầu bằng việc học những lệnh cơ bản, và sau đó sẽ từ từ tìm hiểu về các vấn đề phức tạp hơn." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "Chỉ với $9.99 USD một tháng, con của bạn sẽ nhận được những thử thách mới mỗi tháng và sẽ nhận được sự hỗ trợ từ các lập trình viên chuyên nghiệp qua email." # {change} + parents_blurb3: "Không hề có rủi ro: Nếu bạn không hài lòng bạn có thể nhận lại 100% số tiền mình bỏ ra chỉ với 1 cú nhấp chuốt." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" + + choose_hero: + choose_hero: "Hãy chọn một nhân vật" + programming_language: "Ngôn ngữ lập trình" + programming_language_description: "Bạn muốn sử dụng ngôn ngữ lập trình nào ?" + default: "Mặc Định" + experimental: "Thử" + python_blurb: "Đơn giản nhưng mạnh mẽ, tốt cho những người mới bắt đầu và cả những chuyên gia." + javascript_blurb: "Ngôn ngữ của thế giới web. (Không giống với Java đâu nhé.)" + coffeescript_blurb: "Thực ra là JavaScript nhưng với cú pháp tốt hơn." + clojure_blurb: "Lisp thời đại mới." + lua_blurb: "Ngôn ngữ hay dùng trong làm game." # io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" + status: "Tình trạng" + hero_type: "Loại" + weapons: "Vũ khí" + weapons_warrior: "Kiếm - Tầm ngắn, Không có phép thuật" + weapons_ranger: "Nỏ, Súng - Tầm xa, Không có phép thuật" + weapons_wizard: "Đũa thần, Gậy quyền lực - Tầm xa, Có phép thuật" + attack: "Tấn công" # Can also translate as "Attack" + health: "Máu" + speed: "Tốc độ" # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + blocks: "Đỡ" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" + skills: "Những kĩ năng" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." -# skill_docs: + skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this # read_only: "read-only" -# action_name: "name" + action_name: "tên" # action_cooldown: "Takes" # action_specific_cooldown: "Cooldown" # action_damage: "Damage" # action_range: "Range" -# action_radius: "Radius" + action_radius: "Bán kính" # action_duration: "Duration" -# example: "Example" + example: "Ví dụ" # ex: "ex" # Abbreviation of "example" # current_value: "Current Value" # default_value: "Default value" @@ -369,23 +521,21 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # returns: "Returns" # granted_by: "Granted by" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + save_load: + granularity_saved_games: "Đã lưu" + granularity_change_history: "Lịch sử" -# options: + options: # general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." + volume_label: "Âm lượng" + music_label: "Âm nhạc" + music_description: "Bật/tắt nhạc nền." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." + editor_config_level_language_label: "Ngôn ngữ lập trình cho bàn này" + editor_config_level_language_description: "Hãy chọn ngôn ngữ lập trình bạn sẽ sử dụng cho bàn này." + editor_config_default_language_label: "Ngôn ngữ lập trình mặc định" + editor_config_default_language_description: "Hãy chọn ngôn ngữ lập trình bạn sẽ sử dụng khi bắt đầu các bàn mới." # editor_config_keybindings_label: "Key Bindings" # editor_config_keybindings_default: "Default (Ace)" # editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." @@ -398,8 +548,8 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # editor_config_behaviors_label: "Smart Behaviors" # editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." -# about: -# why_codecombat: "Why CodeCombat?" + about: + why_codecombat: "Tại sao lại sử dụng CodeCombat?" # why_paragraph_1: "If you want to learn to program, you don't need lessons. You need to write a lot of code and have a great time doing it." # why_paragraph_2_prefix: "That's what programming is about. It's gotta be fun. Not fun like" # why_paragraph_2_italic: "yay a badge" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" + george_title: "Tổng giám đốc điều hành" # {change} # george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" + scott_title: "Lập trình viên" # {change} + scott_blurb: "Một người có lý trí, biết suy luận" + nick_title: "Lập trình viên" # {change} # nick_blurb: "Motivation Guru" -# michael_title: "Programmer" + michael_title: "Lập trình viên" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + matt_title: "Lập trình viên" # {change} + matt_blurb: "Một người thích đi xe đạp" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + + teachers: + title: "Thông tin về CodeCombat dành cho giáo viên" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." + sys_requirements_2: "Hãy sử dụng phiên bản mới nhất của Chrome hoặc Firefox." # {change} + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "Lưu Phiên bản Mới" new_major_version: "Phiên bản chính mới" + submitting_patch: "Đang gửi một bản vá..." cla_prefix: "Để lưu thay đổi, bạn phải chấp thuận với chúng tôi trước" -# cla_url: "CLA" -# cla_suffix: "." + cla_url: "CLA" + cla_suffix: "." cla_agree: "TÔI ĐỒNG Ý" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "Liên hệ CodeCombat" welcome: "Rất vui được nhận tin từ bạn! Hãy dùng đơn này để gởi mail cho chúng tôi. " - contribute_prefix: "Nếu bạn muốn đóng góp, hãy kiểm tra " - contribute_page: "trang đóng góp" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " forum_page: "Diễn đàn của chúng tôi" # forum_suffix: " instead." +# faq_prefix: "There's also a" + faq: "Những câu hỏi thường gặp" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "Gởi phản hồi" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -448,14 +692,21 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn title: "Cài đặt Tài khoản" not_logged_in: "Đăng nhập hoặc tạo tài khoản để thay đổi cài đặt." autosave: "Tự động lưu thay đổi" -# me_tab: "Me" + me_tab: "Tôi" picture_tab: "Bức tranh" -# upload_picture: "Upload a picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" + upload_picture: "Tải ảnh lên" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "Mật khẩu" emails_tab: "Emails" -# admin: "Admin" + admin: "Admin" new_password: "Mật khẩu mới" new_password_verify: "Xác nhận" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "Thuê bao Email" # email_subscriptions_none: "No Email Subscriptions." email_announcements: "Thông báo" @@ -465,7 +716,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # email_any_notes: "Any Notifications" # email_any_notes_description: "Disable to stop all activity notification emails." # email_news: "News" -# email_recruit_notes: "Job Opportunities" + email_recruit_notes: "Cơ hội việc làm" # email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." # contributor_emails: "Contributor Class Emails" contribute_prefix: "Chúng tôi đang tìm thêm người vào nhóm của chúng tôi! Hãy kiểm " @@ -475,21 +726,20 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn error_saving: "Lỗi lưu" saved: "Thay đổi được lưu" password_mismatch: "Mật khẩu không khớp." -# password_repeat: "Please repeat your password." + password_repeat: "Hãy nhập lại mật khẩu của bạn một lần nữa." # job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated # job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" - wizard_tab: "Wizard" - wizard_color: "Màu trang phục Wizard" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" + keyboard_shortcuts: + keyboard_shortcuts: "Các phím tắt" + space: "Phím Space" + enter: "Phím Enter" +# press_enter: "press enter" + escape: "Phím Escape" + shift: "Phím Shift" # run_code: "Run current code." # run_real_time: "Run in real time." # continue_script: "Continue past current script." @@ -503,10 +753,9 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." -# community: -# main_title: "CodeCombat Community" + community: + main_title: "Cộng đồng CodeCombat" # introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" # level_editor_prefix: "Use the CodeCombat" # level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" @@ -517,62 +766,127 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # find_us: "Find us on these sites" # social_blog: "Read the CodeCombat blog on Sett" # social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" + social_facebook: "Hãy thích trang Facebook của CodeCombat" + social_twitter: "Theo dõi CodeCombat trên Twitter" # social_gplus: "Join CodeCombat on Google+" # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: # archmage_title: "Archmage" -# archmage_title_description: "(Coder)" + archmage_title_description: "(Lập trình viên)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" + artisan_title_description: "(Người xây dựng những bàn mới)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." + adventurer_title: "Nhà thám hiểm" + adventurer_title_description: "(Người chơi thử những bàn mới)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" + scribe_title_description: "(Người chỉnh sửa những bài viết)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." + diplomat_title: "Nhà Ngoại Giao" diplomat_title_description: "(Người phiên dịch)" + diplomat_summary: "CodeCombat đã được địa phương hóa cho hơn 45 ngôn ngữ nhờ sự giúp đỡ của những nhà ngoại giao. Hãy giúp chúng tôi với việc dịch thuật nếu bạn có thể." # ambassador_title: "Ambassador" ambassador_title_description: "(Hỗ trợ)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." -# editor: + editor: # main_title: "CodeCombat Editors" # article_title: "Article Editor" # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" + indoor: "Trong nhà" + desert: "Sa mạc" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" + small: "Bé" + large: "Lớn" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" # level_tab_settings: "Settings" # level_tab_components: "Components" -# level_tab_systems: "Systems" + level_tab_systems: "Những hệ thống" # level_tab_docs: "Documentation" # level_tab_thangs_title: "Current Thangs" # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" -# delete: "Delete" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" + delete: "Xóa" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" # level_component_btn_new: "Create New Component" -# level_systems_tab_title: "Current Systems" + level_systems_tab_title: "Những hệ thống hiện tại" # level_systems_btn_new: "Create New System" # level_systems_btn_add: "Add System" # level_components_title: "Back to All Thangs" @@ -581,60 +895,60 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # level_component_config_schema: "Config Schema" # level_component_settings: "Settings" # level_system_edit_title: "Edit System" -# create_system_title: "Create New System" + create_system_title: "Tạo một hệ thống mới" # new_component_title: "Create New Component" -# new_component_field_system: "System" -# new_article_title: "Create a New Article" + new_component_field_system: "Hệ thống" + new_article_title: "Tạo một bài báo mới" # new_thang_title: "Create a New Thang Type" -# new_level_title: "Create a New Level" -# new_article_title_login: "Log In to Create a New Article" + new_level_title: "Tạo một bàn mới" + new_article_title_login: "Hãy đăng nhập để tạo một bài báo mới" # new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" + new_level_title_login: "Hãy đăng nhập để tạo một bàn mới" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" + tasks: "Những nhiệm vụ" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" -# article: -# edit_btn_preview: "Preview" -# edit_article_title: "Edit Article" + article: + edit_btn_preview: "Xem trước" + edit_article_title: "Chỉnh sửa bài viết" -# contribute: +# polls: +# priority: "Priority" + + contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" + intro_blurb: "CodeCombat là một sản phẩm mã nguồn mở 100% ! Nhờ sự giúp đỡ của hàng trăm người chơi tốt bụng, chúng tôi mới có thể xây dựng trò chơi này được như ngày hôm nay. Hãy tham gia cùng chúng tôi để giúp CodeCombat có thể hoàn thành sứ mệnh lớn lao của nó là dạy lập trình đến tất cả mọi người trên thế giới !" + alert_account_message_intro: "Này bạn!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " # archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." # archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" + how_to_join: "Cách tham gia" # join_desc_1: "Anyone can help out! Just check out our " # join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " # join_desc_3: ", or find us in our " # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" -# adventurer_forum_url: "our forum" + adventurer_forum_url: "diễn đàn của chúng tôi" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,27 +984,24 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" # powerful_archmages: "Our Powerful Archmages:" # creative_artisans: "Our Creative Artisans:" -# brave_adventurers: "Our Brave Adventurers:" + brave_adventurers: "Những nhà thám hiểm dũng cảm:" # translating_diplomats: "Our Translating Diplomats:" # helpful_ambassadors: "Our Helpful Ambassadors:" -# ladder: + ladder: # please_login: "Please log in first before playing a ladder game." -# my_matches: "My Matches" + my_matches: "Những trận đấu của tôi" # simulate: "Simulate" # simulation_explanation: "By simulating games you can get your game ranked faster!" # simulate_games: "Simulate Games!" @@ -706,8 +1010,8 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # games_simulated_for: "Games simulated for you:" # games_simulated: "Games simulated" # games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" + ratio: "Tỷ lệ" + leaderboard: "Bạng xếp hạng" # battle_as: "Battle as " # summary_your: "Your " # summary_matches: "Matches - " @@ -740,29 +1044,32 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" -# rules: "Rules" + rules: "Những điều lệ" # winners: "Winners" -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" + user: + stats: "Chỉ số" + singleplayer_title: "Những bàn chơi đơn" + multiplayer_title: "Những bàn đa người chơi" + achievements_title: "Những thành tích" + last_played: "Lần chơi cuối cùng" + status: "Tình trạng" + status_completed: "Đã hoàn thành" + status_unfinished: "Chưa hoàn thành" # no_singleplayer: "No Singleplayer games played yet." # no_multiplayer: "No Multiplayer games played yet." # no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." + favorite_prefix: "Ngôn ngữ lập trình ưu thích là " + favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,19 +1284,19 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." -# ladder_prizes: -# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. + ladder_prizes: + title: "Các giải thưởng của cuộc thi" # This section was for an old tournament and doesn't need new translations now. # blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" + blurb_2: "Luật của cuộc thi" # blurb_3: "to the top human and ogre players." # blurb_4: "Two teams means double the prizes!" # blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" + rank: "Hạng" + prizes: "Giải thưởng" # total_value: "Total Value" -# in_cash: "in cash" + in_cash: "tiền mặt" # custom_wizard: "Custom CodeCombat Wizard" # custom_avatar: "Custom CodeCombat avatar" # heap: "for six months of \"Startup\" access" @@ -948,38 +1306,22 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "Cài đặt Wizard" - customize_avatar: "Tùy chỉnh Avatar của bạn" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - -# account_profile: + account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" -# done_editing: "Done Editing" + done_editing: "Đã chỉnh sửa xong" # profile_for_prefix: "Profile for " # profile_for_suffix: "" # featured: "Featured" # not_featured: "Not Featured" # looking_for: "Looking for:" # last_updated: "Last updated:" -# contact: "Contact" + contact: "Liên lạc" # active: "Looking for interview offers now" # inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" -# next_city: "city?" + complete: "Hoàn tất" + next: "Tiếp" + next_city: "Thành phố?" # next_country: "pick your country." # next_name: "name?" # next_short_description: "write a short description." @@ -992,10 +1334,10 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # next_photo: "add an optional professional photo." # next_active: "mark yourself open to offers to show up in searches." # example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" + example_personal_site: "Trang cá nhân" + links_header: "Đường truyền cá nhân" # links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" + links_name: "Tên đường truyền" # links_name_help: "What are you linking to?" # links_link_blurb: "Link URL" # basics_header: "Update basic info" @@ -1003,7 +1345,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # basics_active_help: "Want interview offers right now?" # basics_job_title: "Desired Job Title" # basics_job_title_help: "What role are you looking for?" -# basics_city: "City" + basics_city: "Thành phố" # basics_city_help: "City you want to work in (or live in now)." # basics_country: "Country" # basics_country_help: "Country you want to work in (or live in now)." @@ -1016,41 +1358,41 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # basics_looking_for_contracting: "Contracting" # basics_looking_for_internship: "Internship" # basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" + name_header: "Điền tên của bạn" # name_anonymous: "Anonymous Developer" # name_help: "Name you want employers to see, like 'Nick Winter'." # short_description_header: "Write a short description of yourself" # short_description_blurb: "Add a tagline to help an employer quickly learn more about you." # short_description: "Tagline" # short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" + skills_header: "Những kĩ năng" # skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" + long_description_header: "Mô tả công việc mà bạn muốn làm" # long_description_blurb: "Tell employers how awesome you are and what role you want." # long_description: "Self Description" # long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." -# work_experience: "Work Experience" + work_experience: "Kinh Nghiệm làm việc" # work_header: "Chronicle your work history" # work_years: "Years of Experience" # work_years_help: "How many years of professional experience (getting paid) developing software do you have?" # work_blurb: "List your relevant work experience, most recent first." # work_employer: "Employer" # work_employer_help: "Name of your employer." -# work_role: "Job Title" + work_role: "Nghề nghiệp" # work_role_help: "What was your job title or role?" # work_duration: "Duration" # work_duration_help: "When did you hold this gig?" # work_description: "Description" # work_description_help: "What did you do there? (140 chars; optional)" -# education: "Education" + education: "Trình độ học vấn" # education_header: "Recount your academic ordeals" # education_blurb: "List your academic ordeals." -# education_school: "School" + education_school: "Trường" # education_school_help: "Name of your school." -# education_degree: "Degree" + education_degree: "Bằng cấp" # education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" + education_duration: "Ngày" + education_duration_help: "Khi nào?" # education_description: "Description" # education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" # our_notes: "CodeCombat's Notes" @@ -1061,30 +1403,30 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # projects_blurb: "Highlight your projects to amaze employers." # project_name: "Project Name" # project_name_help: "What was the project called?" -# project_description: "Description" + project_description: "Mô tả" # project_description_help: "Briefly describe the project." -# project_picture: "Picture" + project_picture: "Hình Ảnh" # project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" + project_link: "Link" # project_link_help: "Link to the project." # player_code: "Player Code" -# employers: + employers: # deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." # deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." # hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. -# get_started: "Get Started" + get_started: "Bắt Đầu" # already_screened: "We've already technically screened all our candidates" # filter_further: ", but you can also filter further:" -# filter_visa: "Visa" + filter_visa: "Visa" # filter_visa_yes: "US Authorized" # filter_visa_no: "Not Authorized" # filter_education_top: "Top School" -# filter_education_other: "Other" + filter_education_other: "Khác" # filter_role_web_developer: "Web Developer" # filter_role_software_developer: "Software Developer" # filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" + filter_experience: "Kinh Nghiệm" # filter_experience_senior: "Senior" # filter_experience_junior: "Junior" # filter_experience_recent_grad: "Recent Grad" @@ -1099,18 +1441,18 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # pass_screen: "They will pass your technical screen." # pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." # make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" + what: "CodeCombat là gì?" # what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." # cost: "How much do we charge?" # cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." -# candidate_name: "Name" -# candidate_location: "Location" -# candidate_looking_for: "Looking For" -# candidate_role: "Role" + candidate_name: "Tên" + candidate_location: "Địa điểm" + candidate_looking_for: "Đang tìm kiếm" + candidate_role: "Vai trò" # candidate_top_skills: "Top Skills" -# candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" -# candidate_who: "Who" + candidate_years_experience: "Năm kinh nghiệm" + candidate_last_updated: "Lần cuối cập nhật" + candidate_who: "Người nào" # featured_developers: "Featured Developers" # other_developers: "Other Developers" # inactive_developers: "Inactive Developers" diff --git a/app/locale/zh-HANS.coffee b/app/locale/zh-HANS.coffee index 3d1b46287..229a7086f 100644 --- a/app/locale/zh-HANS.coffee +++ b/app/locale/zh-HANS.coffee @@ -1,16 +1,17 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese (Simplified)", translation: home: slogan: "通过游戏学习编程" - no_ie: "抱歉! Internet Explorer 8 等旧式预览器无法使用本网站。" # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat 不是针对手机设备设计的,所以可能无法达到最好的体验!" # Warning that shows up on mobile devices - play: "开始游戏" # The big play button that just starts playing a level + no_ie: "抱歉! Internet Explorer 8 等老式浏览器无法使用本网站。" # Warning that only shows up in IE8 and older + no_mobile: "CodeCombat不是针对手机设备设计的,所以可能无法达到最好的体验!" # Warning that shows up on mobile devices + play: "开始游戏" # The big play button that opens up the campaign view. old_browser: "噢, 你的浏览器太老了, 不能运行CodeCombat. 抱歉!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "你可以继续重试下去,但八成不起作用,更新浏览器吧亲~" + ipad_browser: "坏消息:CodeCombat无法在iPad的浏览器中运行。好消息:我们的iPad应用正在等待苹果公司审批。" campaign: "战役模式" for_beginners: "适合初学者" multiplayer: "多人游戏" # Not currently shown on home page for_developers: "适合开发者" # Not currently shown on home page. - or_ipad: "或者下载iPad版本" # Or download for iPad + or_ipad: "或下载入iPad" nav: play: "关卡选择" # The top nav bar entry where players choose which levels to play @@ -39,9 +40,9 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese page_not_found: "找不到网页" diplomat_suggestion: - title: "帮助我们翻译 CodeCombat" # This shows up when a player switches to a non-English language using the language selector. + title: "帮助我们翻译CodeCombat" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "我们需要您的语言技能" - pitch_body: "我们开发了 CodeCombat 英文版,但是现在我们的玩家遍布全球。很多人英语不熟练,所以很想玩简体中文版的游戏,如果你中英文都很熟练,请考虑参加我们的翻译工作,帮忙把 CodeCombat 网站和所有关卡翻译成简体中文。" + pitch_body: "我们开发了英文版的CodeCombat,但是CodeCombat的玩家遍布全球。对于不熟悉英语的中文玩家,他们希望能玩简体中文版的CodeCombat,如果你中英文都很熟练,考虑加入我们“外交官”行列并参与CodeCombat的翻译,帮忙把CodeCombat网站和所有关卡翻译成简体中文。感谢!" missing_translations: "没被翻译的文字将以英文显示。" learn_more: "了解更多成为翻译人员的说明" subscribe_as_diplomat: "提交翻译人员申请" @@ -56,67 +57,65 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese confirm: "确认" owned: "已拥有" # For items you own locked: "需解锁" + purchasable: "可购买" # For a hero you unlocked but haven't purchased available: "可用" skills_granted: "获得技能" # Property documentation details heroes: "英雄" # Tooltip on hero shop button from /play achievements: "成就" # Tooltip on achievement list button from /play account: "账户" # Tooltip on account button from /play settings: "设置" # Tooltip on settings button from /play + poll: "投票" # Tooltip on poll button from /play next: "下一步" # Go from choose hero to choose inventory before playing a level change_hero: "重新选择英雄" # Go back from choose inventory to choose hero choose_inventory: "装备道具" buy_gems: "购买宝石" - older_campaigns: "旧的战役" + subscription_required: "需订阅" anonymous: "匿名玩家" level_difficulty: "难度:" campaign_beginner: "新手作战" - awaiting_levels_adventurer_prefix: "我们每周开放五个关卡" + awaiting_levels_adventurer_prefix: "我们每周开放五个关卡" # {change} awaiting_levels_adventurer: "注册成为冒险家" awaiting_levels_adventurer_suffix: "来优先尝试新关卡" - choose_your_level: "选择关卡" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "你可以选择以下任意关卡,或者讨论以上的关卡。到" - adventurer_forum: "冒险者论坛" - adventurer_suffix: "。" - campaign_old_beginner: "旧的新手战役" - campaign_old_beginner_description: "……在这里你可以学习到编程技巧。" - campaign_dev: "随机困难关卡" - campaign_dev_description: "……在这里你可以学到做一些复杂功能的接口。" + adjust_volume: "音量调节" campaign_multiplayer: "多人竞技场" campaign_multiplayer_description: "……在这里你可以与其他玩家进行代码肉搏战。" - campaign_player_created: "创建玩家" - campaign_player_created_description: "……在这里你可以与你的小伙伴的创造力战斗 <a href=\"/contribute#artisan\">技术指导</a>." - campaign_classic_algorithms: "经典算法" - campaign_classic_algorithms_description: "... 你可以在此学习到计算机科学中最常用的算法" - campaign_forest: "森林战役" - campaign_dungeon: "地牢战役" + campaign_old_multiplayer: "(过时的)旧的多人竞技场" + campaign_old_multiplayer_description: "一个文明时代的遗迹。没有模拟运行这些老的、英雄少的多人竞技场。" + + share_progress_modal: + blurb: "你的进度真快!快告诉其他人你从CodeCombat学到了什么!" # {change} + email_invalid: "邮件地址不可用。" + form_blurb: "输入他们的邮件地址,让他们知道CodeCombat的有趣" + form_label: "您的邮件地址" + placeholder: "邮件地址" + title: "你做的太好了!" login: sign_up: "注册" log_in: "登录" logging_in: "正在登录" log_out: "登出" - recover: "找回账户" + forgot_password: "忘记密码?" authenticate_gplus: "使用 G+ 授权" load_profile: "载入 G+ 档案" - load_email: "载入 G+ 电子邮件" finishing: "完成..." + sign_in_with_facebook: "Facebook账号登录" + sign_in_with_gplus: " G+ 账号登录" + signup_switch: "是否创建新账户?" signup: - create_account_title: "创建一个账户来保存进度" - description: "免费而且简单易学:" email_announcements: "通过邮件接收通知" - coppa: "13岁以上或非美国用户" - coppa_why: " 为什么?" creating: "账户创建中……" sign_up: "注册" log_in: "登录" social_signup: "或者,你可以通过Facebook或G+注册:" required: "在做这件事情之前你必须先注册。" + login_switch: "已经注册过账户?" recover: recover_account_title: "找回账户" send_password: "发送重置链接" - recovery_sent: "找回账户邮件已发送." + recovery_sent: "找回账户邮件已发送。" items: primary: "右手" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese books: "书籍" common: + back: "返回" # When used as an action verb, like "Navigate backward" + continue: "继续" # When used as an action verb, like "Continue forward" loading: "读取中……" saving: "保存中……" sending: "发送中……" @@ -139,9 +140,14 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese fork: "派生" play: "开始" # When used as an action verb, like "Play next level" retry: "重试" + actions: "行为" + info: "信息" + help: "帮助" watch: "关注" unwatch: "取消关注" submit_patch: "提交补丁" + submit_changes: "提交更新" + save_changes: "保存更新" general: and: "与" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese date: "日期" body: "正文" version: "版本" + pending: "处理中" + accepted: "已接受" + rejected: "未接受" + withdrawn: "撤回" + submitter: "提交者" + submitted: "已提交" commit_msg: "提交信息" + review: "查看" version_history: "版本历史" version_history_for: "版本历史: " + select_changes: "选择下面两项更新来查看其不同。" + undo_prefix: "取消" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "重做" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "当前关卡预览" result: "结果" results: "结果" description: "描述" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese hard: "困难" player: "玩家" player_level: "等级" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "武士" + ranger: "巡逻兵" + wizard: "巫师" units: second: "秒" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese reload_title: "重载所有代码?" reload_really: "确定重载这一关,返回开始处吗?" reload_confirm: "重载所有" + victory: "胜利" victory_title_prefix: "" victory_title_suffix: " 完成" victory_sign_up: "保存进度" @@ -221,31 +244,31 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese victory_rate_the_level: "评估关卡:" # Only in old-style levels. victory_return_to_ladder: "返回" victory_play_continue: "继续游戏" - victory_play_skip: "跳过并继续" - victory_play_next_level: "下一关" - victory_play_more_practice: "更多练习" - victory_play_too_easy: "太简单" - victory_play_just_right: "刚刚好" - victory_play_too_hard: "太难" victory_saving_progress: "保存进度" victory_go_home: "返回主页" # Only in old-style levels. victory_review: "给我们反馈!" # Only in old-style levels. victory_hour_of_code_done: "你完成了吗?" victory_hour_of_code_done_yes: "是的, 完成了!" + victory_experience_gained: "获得经验" + victory_gems_gained: "获得宝石" + victory_new_item: "新的物品" + victory_viking_code_school: "这关真的超难! 如果你想成为一个软件开发人员,你就应该去试一下Viking Code School。在这里你可以把你的知识增长到另一个台阶。只需要14周你就能成为一个专业的网页开发人员。" + victory_become_a_viking: "成为一个维京人吧" guide_title: "指南" tome_minion_spells: "助手的咒语" # Only in old-style levels. tome_read_only_spells: "只读的咒语" # Only in old-style levels. tome_other_units: "其他单元" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" + tome_cast_button_run: "运行" + tome_cast_button_running: "正在运行" + tome_cast_button_ran: "运行过" tome_submit_button: "提交" tome_reload_method: "重载该方法的原始代码" # Title text for individual method reload button. tome_select_method: "选择方法" - tome_see_all_methods: "查看所有能够编辑的方法" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_see_all_methods: "查看所有能够编辑的方法" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "选择人物来 " tome_available_spells: "可用的法术" tome_your_skills: "你的技能" + tome_help: "帮助" tome_current_method: "当前方法" hud_continue_short: "继续" code_saved: "代码已保存" @@ -254,48 +277,75 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese loading_ready: "载入完成!" loading_start: "开战" problem_alert_title: "修正你的代码" + problem_alert_help: "帮助" time_current: "现在:" time_total: "最大:" time_goto: "跳到:" + non_user_code_problem_title: "不能读入关卡" + infinite_loop_title: "有无限循环" + infinite_loop_description: "建立世界的初始代码永远不会运行完毕。这可能是真的运行的很慢或者陷入了一个死循环。又或者可能存在bug。您可以尝试再次运行该代码,或者将代码重置为默认状态。如果仍旧解决不了,请让告诉我们。" + check_dev_console: "你也可以打开开发者界面看一下有什么可能出错了。" + check_dev_console_link: "(说明)" infinite_loop_try_again: "请重试" infinite_loop_reset_level: "重置等级" infinite_loop_comment_out: "为我的代码添加注释" tip_toggle_play: "用 Ctrl+P 来暂停或继续" - tip_scrub_shortcut: "用 Ctrl+[ 和 Ctrl+] 来倒退和快进." - tip_guide_exists: "点击页面上方的指南, 可以获得更多有用信息." - tip_open_source: "CodeCombat 是 100% 开源的!" - tip_beta_launch: "CodeCombat 开始于 2013的10月份." - tip_think_solution: "思考解决方法, 而不是问题." - tip_theory_practice: "在理论研究中,理论和实践之间是没有区别的。但在实践中,它们是有区别的。 - Yogi Berra" - tip_error_free: "有两种方式可以写出没有错误的程序;但是只有第三种方式能让程序达到预期的效果。 - Alan Perlis" - tip_debugging_program: "如果说调试是清除Bug的过程,那么编码就是放置Bug的过程。- Edsger W. Dijkstra" - tip_forums: "到论坛去告诉我们你的想法!" - tip_baby_coders: "在未来,就算小孩都能成为大法师." - tip_morale_improves: "在士气提升之前会一直进行读取." - tip_all_species: "我们相信学习编程的机会对任何种族都是平等的。" - tip_reticulating: "网格状锯齿。(指 Maxis 开发的许多游戏,如《模拟城市》、《孢子》中,加载进程中跑动时出现的卡顿现象)" + tip_scrub_shortcut: "用 Ctrl+[ 和 Ctrl+] 来倒退和快进。" # {change} + tip_guide_exists: "点击页面上方的指南, 可以获得更多有用信息。" + tip_open_source: "「CodeCombat」是100%开源的!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "CodeCombat开始于2013的10月份。" + tip_think_solution: "思考如何解决, 而不是思考问题。" + tip_theory_practice: "在理论上,理论和实践之间是没有区别的。但在实践上,它们是有区别的。 - Yogi Berra" + tip_error_free: "有两种方式可以写出没有错误的程序;但是只有第三种方式是有效的。 - Alan Perlis" + tip_debugging_program: "如果说调试是清除错误的过程,那么编程就是放置错误的过程。- Edsger W. Dijkstra" + tip_forums: "到论坛去告诉我们你的想法!" + tip_baby_coders: "在未来,就算小孩都能成为大法师。" + tip_morale_improves: "在士气提升之前会一直进行读取。" + tip_all_species: "我们相信每一个人都有同等的机会学习编程" + tip_reticulating: "网格状锯齿。(指Maxis开发的许多游戏,如《模拟城市》、《孢子》中,加载进程中跑动时出现的卡顿现象)" tip_harry: "巫师, " - tip_great_responsibility: "更高的编程技巧也意味着更大的调试责任。" + tip_great_responsibility: "更强的编程技巧也意味着有更高的责任来进行调试。" tip_munchkin: "如果你不吃掉你的蔬菜, 一个小矮人将在你睡着之后来找你。" - tip_binary: "这个世界上只有 10 种人: 那些懂二进制的, 还有那些不懂二进制的." + tip_binary: "这个世界上只有10种人: 那些懂二进制的, 还有那些不懂二进制的。" tip_commitment_yoda: "一个程序员必须有高度的责任感和一颗认真的心。 - 尤达大师" - tip_no_try: "做. 或是不做. 这世上不存在'尝试'这种东西. - 尤达大师" + tip_no_try: "做。 或是不做。 这世上不存在'尝试'这种东西。 - 尤达大师" tip_patience: "你必须要有耐心,年轻的学徒 - 尤达大师" - tip_documented_bug: "一个写在文档里的漏洞不算漏洞, 那是个功能." - tip_impossible: "在事情未完成之前,一切都看似不可能. - 纳尔逊·曼德拉" - tip_talk_is_cheap: "多说无用, 亮出你的代码. - Linus Torvalds" + tip_documented_bug: "一个写在文档里的漏洞不算漏洞, 那是个特色。" + tip_impossible: "在事情未完成之前,一切都看似不可能。 - 纳尔逊·曼德拉" + tip_talk_is_cheap: "多说无用, 亮出你的代码。 - Linus Torvalds" tip_first_language: "你所经历过最可怕的事情是你的第一门编程语言。 - Alan Kay" tip_hardware_problem: "问:换一个电灯泡需要几位程序员参与?答:一个都不需要,因为这属于硬件问题。" tip_hofstadters_law: "侯世达定律:做事所花费的时间总是比你预期的要长,即使你的预期中考虑了侯世达定律。" tip_premature_optimization: "过早的优化是万恶之源。 - 高德纳" tip_brute_force: "拿不准时就用穷举法。 - Ken Thompson" - customize_wizard: "自定义向导" + tip_extrapolation: "世界上只有两类人:一类人能够根据不完整的数据进行推断……" + tip_superpower: "编程是我们拥有的最接近超能力的技能" + tip_control_destiny: "拥有控制自己命运的权利才是真正的开源。 - Linus Torvalds" + tip_no_code: "没有什么比不写代码来的更快。" + tip_code_never_lies: "代码从来不会说谎,但是注释有时候会。 — Ron Jeffries" + tip_reusable_software: "软件能够重用的前提是,这货必须能用。" + tip_optimization_operator: "每个编程语言都有注释符号,大多数语言采用的是‘//’" + tip_lines_of_code: "以代码行数为标准管理程序开发的进度跟用重量为标准管理飞船建造的进度一样愚蠢。 — Bill Gates" + tip_source_code: "我想改变世界,但是他们不给我源代码。" + tip_javascript_java: "Java和JavaScript的关系就好比是马和马云。 - Chris Heilmann" + tip_move_forward: "不管你做什么,前进就对了。 - Martin Luther King Jr." + tip_google: "有问题又不能解决?谷歌搜下就行了!" + tip_adding_evil: "加入万恶的压力。" + tip_hate_computers: "那些认为他们讨厌电脑的人,其实他们讨厌的是垃圾程序编写员。- Larry Niven" + tip_open_source_contribute: "你可以帮助「CodeCombat」提高!" + tip_recurse: "迭代为人,递归为神 - L. Peter Deutsch" + tip_free_your_mind: "丢掉一切私心杂念,丢掉害怕、疑问和拒信,解放你的思想。 - Morpheus" + tip_strong_opponents: "即使是最强大的对手也是有弱点的. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: inventory_tab: "道具箱" save_load_tab: "保存/打开" options_tab: "设置" guide_tab: "使用向导" + guide_video_tutorial: "视频教程" + guide_tips: "小技巧" multiplayer_tab: "多人游戏" auth_tab: "注册" inventory_caption: "装备你的英雄" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese multiplayer_caption: "与你的朋友一起玩!" auth_caption: "保存进度" + leaderboard: + leaderboard: "排行榜" + view_other_solutions: "查看其它解答" # {change} + scores: "分数" + top_players: "顶尖玩家是" + day: "今天" + week: "这个星期" + all: "实时" + time: "时间" + damage_taken: "接收的伤害" + damage_dealt: "造成的伤害" + difficulty: "难度" + gold_collected: "收集的金币" + inventory: choose_inventory: "装备道具" equipped_item: "已装备" + required_purchase_title: "需要" available_item: "可用" restricted_title: "被限制" should_equip: "(双击装备此道具)" @@ -316,15 +381,89 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese locked: "(需解锁)" restricted: "(本关卡不得使用)" equip: "装备" - unequip: "取消装备" + unequip: "脱下" buy_gems: few_gems: "几个宝石" pile_gems: "一堆宝石" chest_gems: "一箱宝石" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + purchasing: "正在付款..." + declined: "您的卡片被拒绝" + retrying: "服务器错误,请重试" + prompt_title: "没有足够数量的宝石" + prompt_body: "还需要更多吗?" + prompt_button: "进入商店" + recovered: "之前购买的宝石已恢复。请刷新页面。" + price: "x3500 / 月" + + subscribe: + comparison_blurb: "亲,订阅CodeCombat,大力的提升你的技能!" + feature1: "60+ 基本关卡(4个世界)" # {change} + feature2: "7 个强大 <strong>英雄</strong>以及各式非凡技能!" # {change} + feature3: "30+ 奖励关卡" # {change} + feature4: "每月享有3500额外宝石" + feature5: "视频教学" + feature6: "专业邮件支援" + feature7: "私人 <strong>氏族</strong>" + free: "免费" + month: "月" + subscribe_title: "订阅" + unsubscribe: "取消订阅" + confirm_unsubscribe: "确认取消订阅" + never_mind: "没关系,我们依然喜欢你" + thank_you_months_prefix: "感谢您这" + thank_you_months_suffix: "个月以来对我们的支持。" + thank_you: "感谢您支持CodeCombat." + sorry_to_see_you_go: "真舍不得亲离开啊! 请一定要让我们知道哪里做的还不够好。" + unsubscribe_feedback_placeholder: "噢, 我们做了神马?" + parent_button: "请求你的父母" + parent_email_description: "我们会发送邮件给你的父母让他们帮你订阅CodeCombat。" + parent_email_input_invalid: "邮件地址不正确。" + parent_email_input_label: "父母邮件地址" + parent_email_input_placeholder: "输入父母邮件地址" + parent_email_send: "发送邮件" + parent_email_sent: "邮件已发送!" + parent_email_title: "什么是你父母的邮件地址?" + parents: "致家长" + parents_title: "您的孩子将要学习编写程序。" # {change} + parents_blurb1: "通过使用CodeCombat,您的孩子将学习编写真正的程序代码。他们将学到简单指令,进而处理更复杂的问题。" + parents_blurb1a: "不用怀疑计算机编程能力将是您的孩子作为一个成年人的基本技能。到2020年,77%的工作将会需要编码能力,并且软件工程师将在世界各地成为高需求职业。你知道吗,计算机科学是收入最高的大学学位?" + parents_blurb2: "每月支付9.9美元,他们每周都会有新的挑战,并且通过电子邮件获得专业程序员的指导。" # {change} + parents_blurb3: "无风险承诺:100%退款,一键退款。" + payment_methods: "付费方式" + payment_methods_title: "可接受的付款方式" + payment_methods_blurb1: "我们现有的付费方式有信用卡和支付宝" + payment_methods_blurb2: "如果您想用其他付费方式,请联系我们" + stripe_description: "每月订阅" + subscription_required_to_play: "订阅后才可开始本关" + unlock_help_videos: "订阅后才可以解锁视频教学哦!" + personal_sub: "个人订阅" # Accounts Subscription View below + loading_info: "正在读入订阅内容..." + managed_by: "管理" + will_be_cancelled: "将被取消" + currently_free: "您目前有一个免费订阅" + currently_free_until: "您目前有一个免费订阅,直到" + was_free_until: "您有过一个免费订阅,直到" + managed_subs: "管理订阅" + managed_subs_desc: "为其他玩家(学生、儿童等)添加订阅。" + managed_subs_desc_2: "收件人必须有一个与您提供的电子邮件地址相关联的codecombat帐户。" + group_discounts: "团购价" + group_discounts_1: "我们还为批量订阅提供团购价" + group_discounts_1st: "1个订阅" + group_discounts_full: "全价" + group_discounts_2nd: "2-11个订阅" + group_discounts_20: "八折" + group_discounts_12th: "12+个订阅" + group_discounts_40: "六折" + subscribing: "订阅中..." + recipient_emails_placeholder: "请输入电子邮件地址来订阅,每行一个。" + subscribe_users: "订阅用户" + users_subscribed: "用户订阅:" + no_users_subscribed: "没有用户订阅,请仔细检查您的电子邮件地址。" + current_recipients: "当前收件人" + unsubscribing: "取消订阅中..." + subscribe_prepaid: "点击订阅来使用预付费代码" + using_prepaid: "使用预付费代码进行每月订阅" choose_hero: choose_hero: "请选择您的英雄" @@ -334,40 +473,53 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese experimental: "实验性的" python_blurb: "简单而强大, Python是一个伟大的通用编程语言。" javascript_blurb: "为web开发而生的语言。" - coffeescript_blurb: "一种更好的JavaScript语法." + coffeescript_blurb: "一种更好的JavaScript语法。" clojure_blurb: "一种现代的列表处理语言。" lua_blurb: "一种游戏脚本语言。" io_blurb: "简单而晦涩。" status: "状态" + hero_type: "种类" weapons: "武器" weapons_warrior: "刀剑 - 近程物理攻击" weapons_ranger: "弓弩,火枪 - 远程物理攻击" weapons_wizard: "魔棒,法杖 - 远程魔法攻击" - attack: "伤害" # Can also translate as "Attack" + attack: "攻击" # Can also translate as "Attack" health: "健康值" speed: "速度" regeneration: "恢复" range: "攻击范围" # As in "attack or visual range" blocks: "格挡" # As in "this shield blocks this much damage" + backstab: "背刺" # As in "this dagger does this much backstab damage" skills: "技能" + attack_1: "交易" + attack_2: "列表" + attack_3: "武器攻击力." + health_1: "收益" + health_2: "列表" + health_3: "装备血量." + speed_1: "移动速度" + speed_2: "米每秒." + available_for_purchase: "可以购买" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "未解锁关卡:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "只有特定的英雄可以进入本关。" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + skill_docs: + writable: "可写" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "只读" + action_name: "名称" + action_cooldown: "释放时间" + action_specific_cooldown: "冷却时间" + action_damage: "攻击力" + action_range: "范围" + action_radius: "半径" + action_duration: "持续时间" + example: "例程" + ex: "例如" # Abbreviation of "example" + current_value: "当前值" + default_value: "默认值" + parameters: "参数" + returns: "返回值" + granted_by: "技能来自" save_load: granularity_saved_games: "保存" @@ -378,8 +530,6 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese volume_label: "音量" music_label: "音乐" music_description: "开/关背景音乐" - autorun_label: "自动运行" - autorun_description: "控制是否自动运行代码" editor_config: "编辑器配置" editor_config_title: "编辑器配置" editor_config_level_language_label: "本关卡编程语言" @@ -399,50 +549,144 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese editor_config_behaviors_description: "自动匹配括号、大括号和引号。" about: - why_codecombat: "为什么选择 CodeCombat?" + why_codecombat: "为什么选择CodeCombat?" why_paragraph_1: "如果你想学习如何编程,你根本不需要上课。你需要的是写好多代码,并且享受这个过程。" why_paragraph_2_prefix: "这才是编程的要义。编程应该是有趣的过程。不该是" why_paragraph_2_italic: "哇又一个奖章诶" why_paragraph_2_center: "那种“好玩”,而是" why_paragraph_2_italic_caps: "老妈,我得先把这关打完!" - why_paragraph_2_suffix: "这就是为什么 CodeCombat 是个多人游戏,而不是一个游戏化的编程课。你不停,我们就不停——但这次这是件好事。" + why_paragraph_2_suffix: "这就是为什么CodeCombat是个多人游戏,而不是一个游戏化的编程课。你不停,我们就不停——但这次这是件好事。" why_paragraph_3: "如果你一定要对游戏上瘾,那就对这个游戏上瘾,然后成为科技时代的法师吧。" press_title: "博客/媒体" press_paragraph_1_prefix: "想要报道我们? 您可以自由下载和使用" press_paragraph_1_link: "成套宣传包" - press_paragraph_1_suffix: "里的所有材料. 所有商标和图像的使用都不必事先联系我们。" + press_paragraph_1_suffix: "里的所有材料。 所有商标和图像的使用都不必事先联系我们。" team: "团队" - george_title: "CEO" + george_title: "CEO" # {change} george_blurb: "商人" - scott_title: "程序员" + scott_title: "程序员" # {change} scott_blurb: "理性至上" - nick_title: "程序员" + nick_title: "程序员" # {change} nick_blurb: "充满动力的大牛" michael_title: "程序员" michael_blurb: "系统管理员" - matt_title: "程序员" + matt_title: "程序员" # {change} matt_blurb: "自行车爱好者" + cat_title: "首席关卡设计师" + cat_blurb: "气宗" + josh_title: "游戏设计师" + josh_blurb: "地面是熔岩" + jose_title: "音乐" + jose_blurb: "放轻松" + retrostyle_title: "插画师" + retrostyle_blurb: "复古风格的游戏" + + teachers: + title: "CodeCombat给教师的说明" + intro_1: "CodeCombat 是一个教编程的网上游戏。学生会用编程语言写代码。" + intro_2: "无需经验!" + free_title: "要多少钱?" + cost_china: "CodeCombat的前5个关卡在中国是免费的,在这之后需花费每月9.99美元来访问我们架设在中国专属服务器上的140多个关卡。" + free_1: "有100多个覆盖了所有理论的免费关卡。" + free_2: "包月订阅可以访问视频教程和额外的练习关卡。" + teacher_subs_title: "教师可免费订阅!" + teacher_subs_1: "请联系" # {change} + teacher_subs_2: "教师调查" + teacher_subs_3: "建立您的订阅。" + sub_includes_title: "订阅里包含了什么内容?" + sub_includes_1: "除了100多个基础关卡,学生包月订阅还可以使用这些附加功能:" + sub_includes_2: "超过60个练习关卡" # {change} + sub_includes_3: "视频教学" + sub_includes_4: "优质的电子邮件支持" + sub_includes_5: "10个具有独特技能的新英雄" + sub_includes_6: "每月享有3500额外宝石" + sub_includes_7: "私有团队" + monitor_progress_title: "我要如何查看学生的进度?" + monitor_progress_1: "要查看学生的进度,可以通过建立一个" + monitor_progress_2: "在你的课程。" + monitor_progress_3: "为了添加一个学生,可以给他们发送一个你的团队的邀请链接,这个在" + monitor_progress_4: "页面。" + monitor_progress_5: "当他们加入之后,你可以在你的团队页面看到这些学生的进度汇总。" + private_clans_1: "私有团队为每个学生增加了隐私和详细的进度信息。" + private_clans_2: "为了添加一个私有团队,可以勾选'私有团队'复选框,在你建立一个" + private_clans_3: "。" + who_for_title: "谁是CodeCombat的使用对象呢?" + who_for_1: "我们建议让9岁及以上的学生使用CodeCombat。无需任何编程经验。" + who_for_2: "我们设计CodeCombat来吸引男生女生。" + material_title: "这里有多少素材呢?" + material_china: "约40小时的游戏时间和目前为止超过170个的订阅关卡。" + material_1: "大约25个小时的免费内容和额外的15个小时的订阅内容。" + concepts_title: "包括了哪些理论知识?" + how_much_title: "每月的订阅费用是多少?" + how_much_1: "" + how_much_2: "每月订阅" + how_much_3: "每月$9.99, 并可随时要求退款." + how_much_4: "另外,团体购买者将享受折扣优惠" + how_much_5: "我们接受一次性的打折购买以及每年订阅的团体,比如班级或者学校。请联系" + how_much_6: "来获取详情。" + more_info_title: "我可以在哪里找到更多信息?" + more_info_1: "我们的" + more_info_2: "教师论坛" + more_info_3: "是个与其他使用CodeCombat的教育工作者联系的良好平台。" + sys_requirements_title: "系统需求" + sys_requirements_1: "因为CodeCombat是个游戏,它对于电脑的要求很高以运行的顺畅。我们已经优化过他以便让他能在每一个最新浏览器或是比较旧的电脑跑得顺畅,所以每一个人都能享受CodeCombat带来的乐趣。如上,为了使你顺利的完成Hour of Code的学习,我们建议你:" # {change} + sys_requirements_2: "使用最新版本的Chrome或是Firefox." # {change} + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "保存新版本" new_major_version: "新的重要版本" + submitting_patch: "正在提交补丁..." cla_prefix: "要想保存更改,您必须先同意我们的" cla_url: "贡献者许可协议" cla_suffix: "。" cla_agree: "我同意" + owner_approve: "你所做出的修改必须经拥有者确认才能生效。" contact: contact_us: "联系我们" welcome: "我们很乐意收到你的邮件!请用这个表单给我们发邮件。 " - contribute_prefix: "如果你想贡献什么,请看我们的 " - contribute_page: "贡献页面" - contribute_suffix: "!" forum_prefix: "如果你想发布任何公开的东西, 可以试试" forum_page: "我们的论坛" forum_suffix: "" + faq_prefix: "这里还有一个" + faq: "FAQ" + subscribe_prefix: "如果你需要帮助解决一个关卡,请" + subscribe: "订阅CodeCombat" + subscribe_suffix: "并且我们很乐意给你提供代码相关的帮助" + subscriber_support: "既然你已经订阅了CodeCombat,我们将给你提供优先帮助" + screenshot_included: "包含截屏" + where_reply: "我们应该回复谁?" send: "反馈意见" contact_candidate: "联系参选人" # Deprecated -# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated + recruitment_reminder: "用这张表格来联系你希望面试的求职者。但请记住如果你雇佣了他,CodeCombat会收取这位员工第一年工资的15%作为佣金。佣金需在雇佣此员工时就付清并且在之后的90天内如果此员工离职会100%退款。兼职,远程办公员工,合同工以及实习生都可免除此费用。" # Deprecated account_settings: title: "账户设置" @@ -450,18 +694,25 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese autosave: "自动保存修改" me_tab: "我" picture_tab: "图片" + delete_account_tab: "删除账户" + wrong_email: "错误的邮箱地址" + wrong_password: "密码错误" upload_picture: "上传一张图片" + delete_this_account: "永久删除账户" + god_mode: "上帝模式" password_tab: "密码" emails_tab: "邮件" admin: "管理" new_password: "新密码" new_password_verify: "核实" + type_in_email: "输入你的邮箱地址来确认删除" # {change} + type_in_password: "同样的,输入你的密码。" email_subscriptions: "邮箱订阅" email_subscriptions_none: "取消订阅" email_announcements: "通知" - email_announcements_description: "接收关于 CodeCombat 的邮件。" + email_announcements_description: "接收关于CodeCombat的邮件。" email_notifications: "通知" - email_notifications_summary: "私人定制, 自动通知与您有关的 CodeCombat 活动。" + email_notifications_summary: "私人定制, 自动通知与您有关的CodeCombat活动。" email_any_notes: "任何通知" email_any_notes_description: "取消接收所有活动提醒邮件" email_news: "新消息" @@ -477,17 +728,16 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese password_mismatch: "密码不匹配。" password_repeat: "请重新键入密码。" job_profile: "工作经历" # Rest of this section (the job profile stuff and wizard stuff) is deprecated - job_profile_approved: "你填写的工作经历将由CodeCombat认证. 雇主将看到这些信息,除非你将它设置为不启用状态或者连续四周没有更新." - job_profile_explanation: "你好! 请填写下列信息, 我们将使用它帮你寻找一份软件开发的工作." + job_profile_approved: "你填写的工作经历将由CodeCombat认证。 雇主将看到这些信息,除非你将它设置为不启用状态或者连续四周没有更新。" + job_profile_explanation: "你好! 请填写下列信息, 我们将使用它帮你寻找一份软件开发的工作。" sample_profile: "查看示例" view_profile: "浏览个人信息" - wizard_tab: "巫师" - wizard_color: "巫师 衣服 颜色" keyboard_shortcuts: keyboard_shortcuts: "快捷键" space: "空格" enter: "回车" + press_enter: "按回车键" escape: "Esc" shift: "Shift" run_code: "运行当前代码" @@ -503,58 +753,117 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese toggle_pathfinding: "显示/关闭路径寻找提示" beautify: "利用标准编码格式美化你的代码。" maximize_editor: "最大化/最小化代码编辑器" - move_wizard: "在关卡中移动你的巫师角色。" community: - main_title: "CodeCombat 社区" + main_title: "CodeCombat社区" introduction: "看看下面这些你可以参与的项目,如果有你喜欢的就加入进来吧。 我们期待着与您一起工作。" level_editor_prefix: "使用" level_editor_suffix: "来创建和编辑关卡。你可以通过这个工具来给你的同学,朋友,兄弟姐妹们设计谜题,或者用于教学或比赛。如果你觉得直接开始建立一个关卡可能非常困难,那么可以先从一个现成(但尚未完成)的关卡开始做起。" thang_editor_prefix: "我们管游戏中的单位叫 '物品'。 利用" - thang_editor_suffix: "来改良 CodeCombat 中的原材料。让游戏中的东西可以被捡起来扔出去,改变游戏动画的指向,调整一些东西的生命值,或上传您自制的素材。" + thang_editor_suffix: "来改良CodeCombat中的原材料。让游戏中的东西可以被捡起来扔出去,改变游戏动画的指向,调整一些东西的生命值,或上传您自制的素材。" article_editor_prefix: "你在游戏中发现了错误了吗?想要自己设计一些指令吗?来看看我们的" article_editor_suffix: "来帮助玩家从游戏中学到更多的知识。" find_us: "通过这些站点联系我们" - social_blog: "阅读 CodeCombat 在 Sett 上面的博客" + social_blog: "阅读CodeCombat在Sett上面的博客" social_discource: "在我们的论坛参与讨论" - social_facebook: "关注 CodeCombat 的 Facebook 主页" - social_twitter: "关注 CodeCombat 的 Twitter" - social_gplus: "关注 CodeCombat 的 Google+ 主页" - social_hipchat: "在公共的 CodeCombat HipChat 房间与我们交流" + social_facebook: "关注CodeCombat的Facebook主页" + social_twitter: "关注CodeCombat的Twitter" + social_gplus: "关注CodeCombat的Google+主页" + social_hipchat: "在公共的CodeCombat HipChat聊天室与我们进行交流" contribute_to_the_project: "为项目做贡献" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "大法师" archmage_title_description: "(代码编写人员)" - artisan_title: "工匠" - artisan_title_description: "(关卡建立人员)" + archmage_summary: "如果你是对教育类游戏感兴趣的程序开发者,那么就选择大法师来为CodeCombat编写代码吧!" + artisan_title: "工匠师" + artisan_title_description: "(关卡创造人员)" + artisan_summary: "建立游戏关卡并分享给朋友们。那么就选择工匠来教其他人编程吧。" adventurer_title: "冒险家" adventurer_title_description: "(关卡测试人员)" + adventurer_summary: "提前一周免费游玩我们最新发布的关卡,并帮助我们在发布之前寻找程序错误" scribe_title: "文书" - scribe_title_description: "(提示编辑人员)" + scribe_title_description: "(文档编辑人员)" + scribe_summary: "好代码需要好文档,来自全世界数百万的玩家一起编写,编辑以及提高文档的可读性" diplomat_title: "外交官" diplomat_title_description: "(翻译人员)" + diplomat_summary: "CodeCombat已经被我们的外交官们翻译成45多种世界语言。在翻译的同时体验翻译的乐趣吧!" ambassador_title: "使节" ambassador_title_description: "(用户支持人员)" + ambassador_summary: "指导我们亲爱的论坛用户并为他们的问题引出一个正确的方向。我们的使节将CodeCombat带到这个世界上。" editor: - main_title: "CodeCombat 编辑器" + main_title: "CodeCombat编辑器" article_title: "指令编辑器" thang_title: "物品编辑器" level_title: "关卡编辑器" achievement_title: "目标编辑器" + poll_title: "调查编辑器" back: "后退" revert: "还原" revert_models: "还原模式" pick_a_terrain: "选择地形" - small: "小的" + dungeon: "地牢" + indoor: "室内" + desert: "沙漠" grassy: "草地" +# mountain: "Mountain" +# glacier: "Glacier" + small: "小的" + large: "大的" fork_title: "派生新版本" fork_creating: "正在执行派生..." - generate_terrain: "Generate Terrain" + generate_terrain: "生成地形" more: "更多" wiki: "维基" live_chat: "在线聊天" + thang_main: "主菜单" + thang_spritesheets: "Spritesheets" + thang_colors: "颜色" level_some_options: "有哪些选项?" level_tab_thangs: "物体" level_tab_scripts: "脚本" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese level_tab_thangs_all: "所有" level_tab_thangs_conditions: "启动条件" level_tab_thangs_add: "增加物体" + level_tab_thangs_search: "查找物体" + add_components: "添加组件" + component_configs: "组件配置" + config_thang: "双击配置一个thang" delete: "删除" duplicate: "复制" + stop_duplicate: "停止复制" rotate: "旋转" level_settings_title: "设置" level_component_tab_title: "目前所有组件" @@ -592,100 +906,90 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese new_level_title_login: "登录以创建新关卡" new_achievement_title: "创建新目标" new_achievement_title_login: "登录以创建新目标" + new_poll_title: "创建新调查" + new_poll_title_login: "登陆以创建新调查" article_search_title: "在这里搜索物品" thang_search_title: "在这里搜索物品类型" level_search_title: "在这里搜索关卡" achievement_search_title: "搜索目标" +# poll_search_title: "Search Polls" read_only_warning2: "提示:你不能保存任何编辑,因为你没有登陆" no_achievements: "这个关卡还没有被赋予任何目标。" -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" + achievement_query_misc: "关闭杂项的成就" + achievement_query_goals: "关闭通关的成就" level_completion: "关卡完成" - pop_i18n: "填写 I18N" + pop_i18n: "完善 I18N" + tasks: "任务" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" + done_adding: "添加完毕" article: edit_btn_preview: "预览" edit_article_title: "编辑提示" +# polls: +# priority: "Priority" + contribute: page_title: "贡献" - character_classes_title: "贡献者职业" - introduction_desc_intro: "我们对 CodeCombat 有很高的期望。" - introduction_desc_pref: "我们希望所有的程序员一起来学习和游戏,让其他人也见识到代码的美妙,并且展现出社区的最好一面。我们无法, 而且也不想独自完成这个目标:你要知道, 让 GitHub、Stack Overflow 和 Linux 真正伟大的是它们的用户。为了完成这个目标," - introduction_desc_github_url: "我们把 CodeCombat 完全开源" - introduction_desc_suf: ",而且我们希望提供尽可能多的方法让你来参加这个项目,与我们一起创造。" - introduction_desc_ending: "我们希望你也能一起加入进来!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy 以及 Matt" + intro_blurb: "CodeCombat是完全开源的软体!上千个玩家帮助了我们建设了这整个游戏,就是你今天看到的一样!快加入我们然后为CodeCombat打开新的章节,将世界带领进入编程世代前进吧!" alert_account_message_intro: "你好!" alert_account_message: "想要订阅邮件? 您必须先登录" - archmage_summary: "你对游戏图像、界面设计、数据库和服务器运营、多人在线、物理、声音、游戏引擎性能感兴趣吗?想做一个教别人编程的游戏吗?如果你有编程经验,想要开发 CodeCombat ,那就选择这个职业吧。我们会非常高兴在制作史上最棒编程游戏的过程中得到你的帮助。" - archmage_introduction: "制作游戏时,最令人激动的事莫过于整合诸多东西。图像、音响、实时网络交流、社交网络,从底层数据库管理到服务器运维,再到用户界面的设计和实现。制作游戏有很多事情要做,所以如果你有编程经验, 那么你应该选择这个职业。我们会很高兴在制作史上最好编程游戏的路上有你的陪伴." + archmage_introduction: "制作游戏时,最令人激动的事莫过于整合诸多东西。图像、音响、实时网络交流、社交网络,从底层数据库管理到服务器运维,再到用户界面的设计和实现。制作游戏有很多事情要做,所以如果你有编程经验, 那么你应该选择这个职业。我们会很高兴在制作史上最好编程游戏的路上有你的陪伴。" class_attributes: "职业说明" - archmage_attribute_1_pref: "了解" - archmage_attribute_1_suf: ",或者想要学习。我们的多数代码都是用它写就的。如果你喜欢 Ruby 或者 Python,那你肯定会感到很熟悉。它就是 JavaScript,但它的语法更友好。" - archmage_attribute_2: "编程经验和干劲。我们可以帮你走上正规,但恐怕没多少时间培训你。" + archmage_attribute_1_pref: "了解 " + archmage_attribute_1_suf: ",或者想要学习。我们的多数代码都是用它写成的。如果你喜欢 Ruby 或者 Python,那你肯定会感到非常熟悉。它就是 JavaScript,但它的语法更友好。" + archmage_attribute_2: "编程经验和干劲。我们可以帮你走上正轨,但我们恐怕没多少时间培训你。" how_to_join: "如何加入" join_desc_1: "谁都可以加入!先看看我们的" - join_desc_2: ",然后勾选下面的复选框,这样你就会作为勇敢的大法师收到我们的电邮。如果你想和开发人员聊天或者更深入地参与,可以 " + join_desc_2: ",然后勾选下面的复选框,这样你就会作为勇敢的大法师收到我们的邮件。如果你想和开发人员聊天或者更深入地参与,可以" join_desc_3: " 或者去我们的" join_desc_4: ",然后我们有话好说!" join_url_email: "给我们发邮件" - join_url_hipchat: " HipChat 聊天室" - more_about_archmage: "了解如何成为一名大法师" + join_url_hipchat: " HipChat聊天室" archmage_subscribe_desc: "通过电子邮件获得新的编码机会和公告。" - artisan_summary_pref: "想要设计 CodeCombat 的关卡吗?人们玩的比我们做的快多了!现在我们的关卡编辑器还很基本,所以做起关卡来会有点麻烦,还会有bug。只要你有制作关卡的灵感,不管是简单的for循环还是" - artisan_summary_suf: "这种东西,这个职业都很适合你。" artisan_introduction_pref: "我们必须设计更多的关卡! 大家为了更多的游戏内容在高声呐喊,但是我们靠自己只能创建这些。现在你的电脑就是一关!我们的关卡编辑器刚刚完成了基本功能,所以创造关卡的时候请小心使用。只要你有制作关卡的灵感,不管是简单的for循环还是" artisan_introduction_suf: "这种东西,这个职业都很适合你。" artisan_attribute_1: "任何类似的创建内容经验都有加分,无论是暴雪的关卡编辑器,但这不是必须的条件。" artisan_attribute_2: "渴望完成全部的测试和迭代。要做一个优秀的关卡,你需要把它给别的玩家观察他们怎么玩,然后时刻准备着找到很多细节去打磨。" artisan_attribute_3: "探险家般的忍耐力。我们的关卡编辑器非常的初级,还会有很多不好用的地方。唔,我已经提前告诉你了~" artisan_join_desc: "如下步骤使用关卡编辑器:" - artisan_join_step1: "阅读文档." - artisan_join_step2: "创建一个新关卡 以及探索已经存在的关卡." - artisan_join_step3: "来我们的 HipChat 聊天室寻求帮助." - artisan_join_step4: "吧你的关卡发到论坛让别人给你评价." - more_about_artisan: "了解如何成为一名工匠" + artisan_join_step1: "阅读文档。" + artisan_join_step2: "创建一个新关卡 以及探索已经存在的关卡。" + artisan_join_step3: "来我们的HipChat聊天室寻求帮助。" + artisan_join_step4: "吧你的关卡发到论坛让别人给你评价。" artisan_subscribe_desc: "通过电子邮件获得关卡编辑器更新和公告。" - adventurer_summary: "丑话说在前面,你就是那个挡枪子的,而且你会伤得很重。我们需要人手来测试崭新的关卡,并且提出改进意见。做一个好游戏是一个漫长的过程,没人第一次就能搞对。如果你能忍得了这些,而且身体健壮,那这个职业就是你的了。" -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" + adventurer_introduction: "现在就来了解你的角色吧:你是一辆战车,并将要承担沉重的攻击。我们需要人来尝试下我们新开的关卡以了解怎么样才能使每一样东西更美好。一开始虽然会非常辛苦;可是制造出一个最好的游戏是一个很长的过程,在这个过程中,没有人可以第一次就成功。如果你挺过去了并且自我感觉良好,那么这个位置就是为你而准备的。" + adventurer_attribute_1: "学习的冲动!你想要学好怎么编程,与此同时我们也想要教你怎么编程。虽然你可能会觉得你大多数时间你反而都在教导。这,就是学习。" + adventurer_attribute_2: "魅力!温柔但懂得表达事态;了解并知道如何改进。" + adventurer_join_pref: "无论是与工匠一起工作,或者只是在下面的格子打勾以获得新关卡测试的通知,我们也会发布关于对新关卡的看法。" adventurer_forum_url: "我们的论坛" - adventurer_join_suf: "如果你更喜欢以这些方式被通知, 那就注册吧!" - more_about_adventurer: "了解如何成为一名冒险家" + adventurer_join_suf: "如果你更喜欢以这些方式被通知, 那就在这里注册吧!" adventurer_subscribe_desc: "通过电子邮件获得新关卡通知。" - scribe_summary_pref: "CodeCombat 不只是一堆关卡的集合,它还是玩家们编程知识的来源。这样的话,每个工匠都能链接详尽的文档,以供玩家们学习,类似于" - scribe_summary_suf: "那些。如果你喜欢解释编程概念,那么这个职业很适合你。" -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " - scribe_introduction_url_mozilla: "Mozilla 开发者社区" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." + scribe_introduction_pref: "CodeCombat不只是拥有一堆等级或关卡。它也是个知识的源泉,一个编程的宝典,一个人人都可以参与的,有趣的游戏。比起每个工匠都要详细解释其中的奥秘,他们可以以更简单的方式将自己的东西连接到一篇已经准备好的文章给玩家阅读。 有些东西就像 " + scribe_introduction_url_mozilla: "Mozilla开发者社区" + scribe_introduction_suf: " 被建成。如果你那有趣的想法能把编程的理论简易的带出来,那么这里就是你的所属了。" + scribe_attribute_1: "你需要具备在文字语言上的技能。不仅仅是语法和拼音,重要的是能传达各种各样想法给别人。" contact_us_url: "联系我们" - scribe_join_description: "介绍一下你自己, 比如你的编程经历和你喜欢写什么东西, 我们将从这里开始了解你!!" - more_about_scribe: "了解如何成为一名文书" + scribe_join_description: "介绍下你自己, 比如你的编程经历和你喜欢的东西, 我们将会努力的去了解你!!" scribe_subscribe_desc: "通过电子邮件获得写作新文档的通知。" - diplomat_summary: "很多国家不说英文,但是人们对 CodeCombat 兴致很高!我们需要具有热情的翻译者,来把这个网站上的文字尽快带向全世界。如果你想帮我们走向全球,那这个职业适合你。" diplomat_introduction_pref: "如果说我们从" diplomat_launch_url: "十月的发布" - diplomat_introduction_suf: "中得到了什么启发:那就是全世界的人都对 CodeCombat 很感兴趣。我们召集了一群翻译者,尽快地把网站上的信息翻译成各国文字。如果你对即将发布的新内容很感兴趣,想让你的国家的人们玩上,就快来成为外交官吧。" + diplomat_introduction_suf: "中得到了什么启发:那就是全世界的人都对很喜欢CodeCombat。我们召集了一群翻译者,希望能尽快地把网站上的信息翻译成各国文字。如果你对CodeCombat的内容很感兴趣,想让世界上的每一个人都有机会一同体验,就快来成为外交官吧。" diplomat_attribute_1: "既会说流利的英语,也熟悉自己的语言。编程是一件很复杂的事情,而要翻译复杂的概念,你必须对两种语言都在行!" diplomat_i18n_page_prefix: "你可以在我们的" diplomat_i18n_page: "翻译页面" - diplomat_i18n_page_suffix: "开始翻译游戏,或者在 GitHub 我们的页面上进行。" + diplomat_i18n_page_suffix: "开始翻译游戏,或者在我们的GitHub页面上进行。" diplomat_join_pref_github: "在" - diplomat_github_url: " GitHub " - diplomat_join_suf_github: "找到你的语言文件 (中文的是: codecombat/app/locale/zh-HNAS.coffee),在线编辑它,然后提交一个合并请求。同时,选中下面这个复选框来关注最新的国际化开发!" - more_about_diplomat: "了解如何成为一名外交官" - diplomat_subscribe_desc: "接受有关国际化开发和翻译情况的邮件" - ambassador_summary: "我们要建立一个社区,而当社区遇到麻烦的时候,就要支持人员出场了。我们运用 IRC、电邮、社交网站等多种平台帮助玩家熟悉游戏。如果你想帮人们参与进来,学习编程,然后玩的开心,那这个职业属于你。" - ambassador_introduction: "这是一个正在成长的社区,而你将成为我们与世界的联结点。大家可以通过Olark即时聊天、邮件、参与者众多的社交网络来认识了解讨论我们的游戏。如果你想帮助大家尽早参与进来、获得乐趣、感受CodeCombat的脉搏、与我们同行,那么这将是一个适合你的职业。" - ambassador_attribute_1: "有出色的沟通能力。能够辨识出玩家遇到的问题并帮助他们解决这些问题。与此同时,和我们保持联系,及时反馈玩家的喜恶和愿望!" + diplomat_github_url: "GitHub" + diplomat_join_suf_github: "找到中文语言文件 (简体中文的是:CodeCombat/app/locale/zh-HANS.coffee),在线编辑它,完成后提交一个合并请求。你也可以勾上下面这个复选框来关注最新的国际化开发!" + diplomat_subscribe_desc: "接受有关国际化开发和翻译任务的邮件" + ambassador_introduction: "这是一个正在成长的社区,而你将成为我们与世界的联结点。大家可以通过各种即时聊天平台、邮件、或是参与者众多的社交网络来认识及讨论我们的游戏。如果你喜欢帮助大家,一起参与各类项目,一起获得乐趣。与我们同行,一起感受CodeCombat的脉搏,绝对是一个适合你的职业。" + ambassador_attribute_1: "具备出色的沟通能力。能够分辨出玩家遇到的问题并帮助他们解决问题。与此同时,和我们保持联系,帮助反馈玩家们的喜怒哀乐!" ambassador_join_desc: "介绍一下你自己:你做过什么?你喜欢做什么?我们将从这里开始了解你!" ambassador_join_note_strong: "注意" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" - more_about_ambassador: "了解如何成为一名使节" + ambassador_join_note_desc: "我们其中一个想法是要制造一个当玩家们在解决问题上有难度时可以要求其他比较强的玩家来协助他们的多人游戏。这将会是一个很好的办法让使节们可以更容易的协助玩家!如有任何更新,我们会在第一时间通知你!" ambassador_subscribe_desc: "通过电子邮件获得支持系统的现状,以及多人游戏方面的新进展。" changes_auto_save: "在你勾选复选框后,更改将自动保存。" diligent_scribes: "我们勤奋的文书:" @@ -696,17 +1000,17 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese helpful_ambassadors: "我们亲切的使节:" ladder: - please_login: "请在对奕之前先登录." + please_login: "对战之前请先登录。" my_matches: "我的对手" simulate: "模拟" simulation_explanation: "通过模拟游戏,你可以让你的游戏更快的得到评分。" simulate_games: "模拟游戏!" simulate_all: "重置并模拟游戏!" - games_simulated_by: "你模拟玩过的游戏:" - games_simulated_for: "待你模拟玩的游戏:" - games_simulated: "游戏已模拟" - games_played: "已玩过" -# ratio: "Ratio" + games_simulated_by: "由你模拟过的游戏数:" + games_simulated_for: "为你模拟过的游戏数:" + games_simulated: "模拟次数" + games_played: "被模拟次数" + ratio: "比率" leaderboard: "排行榜" battle_as: "我要加入这一方 " summary_your: "你 " @@ -722,15 +1026,15 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese rank_last_submitted: "已提交" help_simulate: "模拟游戏需要帮助?" code_being_simulated: "你的新代码正在被其他玩家模拟评分。这个将会刷新,作为一个新游戏开始。" -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." + no_ranked_matches_pre: "没有排名对决给 " + no_ranked_matches_post: " 啊哈哟!去更其他玩家打过了再回来这里查看你游戏排名吧!" choose_opponent: "选择一个对手" select_your_language: "选择你使用的语言!" tutorial_play: "玩教程" tutorial_recommended: "如果你从未玩过的话,推荐先玩下教程" tutorial_skip: "跳过教材" tutorial_not_sure: "不知道怎么玩?" - tutorial_play_first: "先玩一次教程." + tutorial_play_first: "先玩一次教程。" simple_ai: "简单电脑" warmup: "热身" friends_playing: "联机" @@ -740,78 +1044,111 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese fight: "战斗!" watch_victory: "观看你的胜利" defeat_the: "击败了" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" + tournament_started: ",锦标赛已开始" + tournament_ends: "锦标赛结束" + tournament_ended: "Tournament ended" tournament_rules: "锦标赛规则" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" + tournament_blurb: "写代码,收金币,建军队,碎敌军,赢奖品,以及在我们奖励多达$40,000的Greed tournament里升级你的事业! 快去查阅详情!" tournament_blurb_criss_cross: "赢得竞拍,建造道路,智胜对手,夺取宝石,在纵横交错锦标赛中完成生涯晋级! 现在就查看详情!" -# tournament_blurb_blog: "on our blog" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" + tournament_blurb_blog: "关注我们的博客" rules: "规则" winners: "胜利者" user: stats: "成就" - singleplayer_title: "单人关卡" - multiplayer_title: "多人关卡" + singleplayer_title: "单人游戏关卡" + multiplayer_title: "多人游戏关卡" achievements_title: "成就" last_played: "最近玩的时间" status: "状态" - status_completed: "完成" + status_completed: "已完成" status_unfinished: "未完成" - no_singleplayer: "还未玩过任何单人关卡。" - no_multiplayer: "还未玩过任何多人关卡。" - no_achievements: "还未得到任何成就" + no_singleplayer: "还未玩过任何单人游戏关卡。" + no_multiplayer: "还未玩过任何多人游戏关卡。" + no_achievements: "还未得到任何成就。" favorite_prefix: "最喜爱的语言是 " - favorite_postfix: "." + favorite_postfix: "。" + not_member_of_clans: "还不是任何一个团队里的成员。" achievements: - last_earned: "最近取得的时间" + last_earned: "最近取得的" amount_achieved: "数量" achievement: "成就" - category_contributor: "贡献" -# category_ladder: "Ladder" -# category_level: "Level" + category_contributor: "贡献者" + category_ladder: "排行榜" + category_level: "等级" category_miscellaneous: "其他" category_levels: "等级" category_undefined: "未分类" - current_xp_prefix: "当前总共" - current_xp_postfix: "经验" - new_xp_prefix: "获得" - new_xp_postfix: "经验" - left_xp_prefix: "还需要" - left_xp_infix: "经验" - left_xp_postfix: "到下一等级" + current_xp_prefix: "" + current_xp_postfix: "当前总共" + new_xp_prefix: "" + new_xp_postfix: "取得的" + left_xp_prefix: "" + left_xp_infix: "直到等级" + left_xp_postfix: "" account: recently_played: "最近玩过的关卡" no_recent_games: "最近两个星期没有玩过游戏。" + payments: "支付方式" + purchased: "已购买" + subscription: "订阅" + invoices: "票据" + service_apple: "设备:苹果" + service_web: "设备:网页" + paid_on: "支付" + service: "服务" + price: "价格" + gems: "宝石" + active: "激活" + subscribed: "已订阅" + unsubscribed: "取消订阅" + active_until: "已激活直到" + cost: "花费" + next_payment: "下个付款" + card: "银行卡" + status_unsubscribed_active: "你还没有订阅所以不会被收费,但你的帐号还可以正常使用" + status_unsubscribed: "只要一个CodeCombat订阅,你就可以玩转全部新关卡,用遍全部英雄,物品,以及获得更多的宝石哦!" + + account_invoices: + amount: "金额(美元)" + declined: "您的付款被拒" + invalid_amount: "请输入美元金额" +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" + purchasing: "购买中……" + retrying: "服务器错误,重试中。" + success: "支付成功。谢谢!" loading_error: could_not_load: "载入失败" - connection_failure: "连接失败." - unauthorized: "你需要登录才行. 你是不是把 cookies 禁用了?" - forbidden: "你没有权限." - not_found: "没找到." - not_allowed: "方法不允许." - timeout: "服务器超时." - conflict: "资源冲突." - bad_input: "坏输入." - server_error: "服务器错误." - unknown: "未知错误." + connection_failure: "连接失败。" + unauthorized: "你需要登录才行。 你是不是把 cookies 禁用了?" + forbidden: "你没有权限。" + not_found: "没找到。" + not_allowed: "方法不允许。" + timeout: "服务器超时。" + conflict: "资源冲突。" + bad_input: "坏输入。" + server_error: "服务器错误。" + unknown: "未知错误。" resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" + sessions: "会期" + your_sessions: "你的会期" level: "等级" social_network_apis: "社交网络 APIs" facebook_status: "Facebook 状态" facebook_friends: "Facebook 朋友" -# facebook_friend_sessions: "Facebook Friend Sessions" + facebook_friend_sessions: "Facebook 朋友会期" gplus_friends: "G+ 朋友" -# gplus_friend_sessions: "G+ Friend Sessions" + gplus_friend_sessions: "G+ 朋友会期" leaderboard: "排行榜" user_schema: "用户模式" user_profile: "用户信息" + patch: "补丁" patches: "补丁" patched_model: "源文档" model: "型号" @@ -821,41 +1158,68 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese components: "组件" thang: "物品" thangs: "物品" -# level_session: "Your Session" -# opponent_session: "Opponent Session" + level_session: "你的会期" + opponent_session: "对方会期" article: "文章" user_names: "用户名" thang_names: "物品名称" files: "文件" -# top_simulators: "Top Simulators" -# source_document: "Source Document" + top_simulators: "顶尖模拟者" + source_document: "原档" document: "文档" sprite_sheet: "SpriteSheet" - employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" + employers: "雇主" + candidates: "应征者" + candidate_sessions: "应征者会期" + user_remark: "用户备注" + user_remarks: "用户备注" + versions: "版本" items: "物品" + hero: "英雄" heroes: "英雄" - wizard: "巫师" achievement: "成就" -# clas: "CLAs" -# play_counts: "Play Counts" + clas: "CLAs" + play_counts: "游戏次数" feedback: "反馈" + payment_info: "付款信息" + campaigns: "任务" + poll: "调查" + user_polls_record: "投票结果" -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" -# guide: -# temp: "Temp" + delta: + added: "被添加" + modified: "被修改" +# not_modified: "Not Modified" + deleted: "被删除" + moved_index: "被移动的索引" + text_diff: "文本变化" + merge_conflict_with: "合并冲突发生在" + no_changes: "没有变化" + + guide: + temp: "临时" multiplayer: multiplayer_title: "多人游戏设置" # We'll be changing this around significantly soon. Until then, it's not important to translate. @@ -863,22 +1227,22 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese multiplayer_toggle_description: "允许其他人加入游戏。" multiplayer_link_description: "把这个链接告诉小伙伴们,一起玩吧。" multiplayer_hint_label: "提示:" - multiplayer_hint: " 点击全选,然后按 Apple-C(苹果电脑)或 Ctrl-C 复制链接。" + multiplayer_hint: " 点击全选,然后按Apple-C(苹果电脑)或Ctrl-C复制链接。" multiplayer_coming_soon: "多人游戏的更多特性!" multiplayer_sign_in_leaderboard: "注册并登录账号,就可以将你的成就发布到排行榜上。" legal: page_title: "法律" - opensource_intro: "CodeCombat 是一个自由发挥,完全开源的项目。" + opensource_intro: "CodeCombat是一个自由发挥,完全开源的项目。" opensource_description_prefix: "查看 " - github_url: "我们的 GitHub" - opensource_description_center: "并做你想做的修改吧!CodeCombat 是构筑在几十个开源项目之上的,我们爱它们。请查阅" - archmage_wiki_url: "我们 大法师的维基页" - opensource_description_suffix: " 看看是哪些人让这个游戏成为可能." + github_url: "我们的GitHub" + opensource_description_center: "并做你想做的修改吧!CodeCombat是构筑在几十个开源项目之上的,然后我们都喜欢它们。看 " + archmage_wiki_url: "我们大法师的维基页" + opensource_description_suffix: " 看看是哪些人让这个游戏成为可能。" practices_title: "尊重最佳实践" practices_description: "这是我们对您的承诺,即玩家,尽管这在法律用语中略显不足。" privacy_title: "隐私" - privacy_description: "我们不会出售您的任何个人信息。我们计划最终通过招聘来盈利,但请您放心,未经您的明确同意,我们不会将您的个人信息出售有兴趣的公司。" + privacy_description: "我们不会泄露您的个人信息。" security_title: "安全" security_description: "我们竭力保证您的个人信息安全性。作为一个开源项目,任何人都可以检讨并改善我们自由开放的网站的安全性。" email_title: "电子邮件" @@ -886,36 +1250,30 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese email_settings_url: "您的电子邮件设置" email_description_suffix: "或者我们发送的邮件中的链接,您可以随时更改您的偏好设置或者随时取消订阅。" cost_title: "花费" - cost_description: "目前来说,CodeCombat 是完全免费的!我们的主要目标之一也是保持目前这种方式,让尽可能多的人玩得更好,不论是否是生活中。如果天空变暗,我们可能会对某些内容采取订阅收费,但我们宁愿不那么做。运气好的话,我们可以维持公司,通过:" - recruitment_title: "招募" - recruitment_description_prefix: "在 CodeCombat 这里,你将得以成为一名法力强大的“巫师”,不只是在游戏中,更在生活中。" - url_hire_programmers: "没有人能以足够快速度招聘程序员," - recruitment_description_suffix: "所以一旦你的技能成熟并且得到你的同意,我们将战士你的最佳编码成就给上万名雇主,希望他们垂涎欲滴。而他们支付给我们一点点报酬,并且付给你工资," - recruitment_description_italic: "“一大笔”" - recruitment_description_ending: "。而这网站也就能保持免费,皆大欢喜。计划就是这样。" + cost_description: "目前来说,CodeCombat是完全免费的!我们的主要目标之一也是保持目前这种方式,让尽可能多的人玩得更好,不论是否是生活中。如果天空变暗,我们可能会对某些内容采取订阅收费,但我们宁愿不那么做。运气好的话,我们可以维持公司,通过:" copyrights_title: "版权与许可" contributor_title: "贡献者许可协议" - contributor_description_prefix: "所有对本网站或是 GitHub 代码库的贡献都依照我们的" + contributor_description_prefix: "所有对本网站或是GitHub代码库的贡献都依照我们的" cla_url: "贡献者许可协议(CLA)" contributor_description_suffix: "而这在您贡献之前就应该已经同意。" code_title: "代码 - MIT" - code_description_prefix: "所有由 CodeCombat 拥有或是托管在 codecombat.com 的代码,在 GitHub 版本库或者 codecombat.com 数据库,以上许可协议都依照" + code_description_prefix: "所有由CodeCombat拥有或是托管在CodeCombat.com的代码,在GitHub版本库或者CodeCombat.com数据库,以上许可协议都依照" mit_license_url: "MIT 许可证" - code_description_suffix: "这包括所有 CodeCombat 公开的制作关卡用的系统和组件代码。" + code_description_suffix: "这包括所有CodeCombat公开的制作关卡用的系统和组件代码。" art_title: "美术和音乐 - Creative Commons" art_description_prefix: "所有共通的内容都在" -# cc_license_url: "Creative Commons Attribution 4.0 International License" - art_description_suffix: "条款下公开。共通内容是指所有 CodeCombat 发布出来用于制作关卡的内容。这包括:" + cc_license_url: "Creative Commons Attribution 4.0 International License" + art_description_suffix: "条款下公开。共通内容是指所有CodeCombat发布出来用于制作关卡的内容。这包括:" art_music: "音乐" art_sound: "声效" art_artwork: "图画" art_sprites: "精灵" art_other: "所有制作关卡时公开的,不是代码的创造性产品。" art_access: "目前还没有简便通用的下载素材的方式。一般来讲,从网站上使用的URL下载,或者联系我们寻找帮助。当然你也可以帮我们扩展网站,让这些资源更容易下载。" - art_paragraph_1: "关于署名,请说明并在使用处附近,或对媒体形式来说合适的地方提供一个 codecombat.com 的链接。举例:" - use_list_1: "如果是用在电影里或者其他游戏里,请在制作人员表中加入 codecombat.com 。" - use_list_2: "如果用在网站上,将链接在使用的地方附近,比如图片下面,或者一个你放置其他 Creative Commons 署名和开源软件协议的专门页面。如果你的内容明确提到关于 CodeCombat,那你就不需要额外署名。" - art_paragraph_2: "如果你使用的内容不是由 CodeCombat 制作,而是由 codecombat.com 上其他的用户制作的,那你应该给他们署名。如果相应资源的页面上有署名指示,那你应该遵循那些指示。" + art_paragraph_1: "关于署名,请说明并在使用处附近,或对媒体形式来说合适的地方提供一个CodeCombat.com 的链接。举例:" + use_list_1: "如果是用在电影里或者其他游戏里,请在制作人员表中加入CodeCombat.com." + use_list_2: "如果用在网站上,将链接在使用的地方附近,比如图片下面,或者一个你放置其他 Creative Commons 署名和开源软件协议的专门页面。如果你的内容明确提到关于CodeCombat,那你就不需要额外署名。" + art_paragraph_2: "如果你使用的内容不是由CodeCombat制作,而是由CodeCombat.com 上其他的用户制作的,那你应该给他们署名。如果相应资源的页面上有署名指示,那你应该遵循那些指示。" rights_title: "版权所有" rights_desc: "所有关卡由他们自己版权所有。这包括" rights_scripts: "脚本" @@ -925,44 +1283,28 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese rights_media: "声音、音乐以及其他专门为某个关卡制作,而不对其他关卡开放的创造性内容" rights_clarification: "澄清:所有在关卡编辑器里公开用于制作关卡的资源都是在CC协议下发布的,而使用关卡编辑器制作,或者在关卡制作过程中上传的内容则不是。" nutshell_title: "简而言之" - nutshell_description: "我们在关卡编辑器里公开的任何资源,你都可以在制作关卡时随意使用,但我们保留限制在 codecombat.com 之上创建的关卡本身传播的权利,因为我们以后可能决定为它们收费。" + nutshell_description: "我们在关卡编辑器里公开的任何资源,你都可以在制作关卡时随意使用,但我们保留限制在CodeCombat.com 之上创建的关卡本身传播的权利,因为我们以后可能决定为它们收费。" canonical: "这篇说明的英文版本是权威版本。如果各个翻译版本之间有任何冲突,以英文版为准。" -# ladder_prizes: -# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - - wizard_settings: - title: "设置向导" - customize_avatar: "设置你的头像" - active: "启用" - color: "颜色" - group: "类别" - clothes: "衣服" - trim: "条纹" - cloud: "云" - team: "队伍" - spell: "法球" - boots: "鞋子" - hue: "颜色" - saturation: "饱和度" - lightness: "亮度" + ladder_prizes: + title: "竞标赛奖项" # This section was for an old tournament and doesn't need new translations now. + blurb_1: "这些奖项将会奖励,依据" + blurb_2: "锦标赛规则" + blurb_3: ", 给顶尖的人类和怪物玩家。" + blurb_4: "两队表示获得的奖励将会是两倍!" + blurb_5: "(将有两个第一名, 两个第二名, etc.)" + rank: "排名" + prizes: "奖项" + total_value: "总价值" + in_cash: "现金" + custom_wizard: "客制CodeCombat巫师" + custom_avatar: "客制CodeCombat头像" + heap: "给六個月的\"Startup\"访问" + credits: "信誉" + one_month_coupon: "固本: 选择 Rails 或者 HTML" + one_month_discount: "折扣 30% : 选择 Rails 或者 HTML" + license: "许可证" + oreilly: "你选择的电子书" account_profile: settings: "设置" # We are not actively recruiting right now, so there's no need to add new translations for this section. @@ -975,134 +1317,134 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese looking_for: "寻找" last_updated: "最后一次更新:" contact: "联系" - active: "正期待面试offer" - inactive: "并不期待面试offer" + active: "正在寻求面试机会" + inactive: "目前没有在寻找新的工作机会" complete: "完成" next: "下一步" next_city: "城市?" - next_country: "选择你的城市。" - next_name: "姓名?" - next_short_description: "写一个简短的描述" - next_long_description: "描述你渴望的职位。" + next_country: "选择你所处的国家" + next_name: "名字?" + next_short_description: "简短描述你自己。" + next_long_description: "描述一下你希望找到何种工作。" next_skills: "列出至少五个技能。" next_work: "你的过往工作经验" next_education: "教育经历" -# next_projects: "show off up to three projects you've worked on." -# next_links: "add any personal or social links." -# next_photo: "add an optional professional photo." -# next_active: "mark yourself open to offers to show up in searches." -# example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" -# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" -# links_name_help: "What are you linking to?" -# links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" -# basics_city: "City" -# basics_city_help: "City you want to work in (or live in now)." -# basics_country: "Country" -# basics_country_help: "Country you want to work in (or live in now)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" -# basics_looking_for_full_time: "Full-time" -# basics_looking_for_part_time: "Part-time" -# basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." + next_projects: "展示你曾经参与过的至多3个项目。" + next_links: "添加任何个人网站或社交媒体链接。" + next_photo: "添加一张职业的照片(可选)。" + next_active: "将自己标记为正在寻求工作机会以使自己的名字出现在搜索结果中。" + example_blog: "你的博客" + example_personal_site: "个人主页" + links_header: "个人网站链接" + links_blurb: "链接任何你希望展示的网站或介绍,例如你的Github,你的LinkedIn,或是你的博客。" + links_name: "链接名称" + links_name_help: "你希望链接到什么?" + links_link_blurb: "链接的URL" + basics_header: "更新基本信息" + basics_active: "接受工作邀请" + basics_active_help: "希望现在就接到面试邀请?" + basics_job_title: "期望职位" + basics_job_title_help: "你希望在工作中扮演何种角色?" + basics_city: "城市" + basics_city_help: "你希望在哪座城市工作(或现在居住在哪座城市)。" + basics_country: "国家" + basics_country_help: "你希望在哪个国家工作(或现在居住在哪个国家)。" + basics_visa: "美国工作签证状态" + basics_visa_help: "你是否有权在美国合法地工作,或者你是否需要公司资助办理美国工作签证?" + basics_looking_for: "寻找" + basics_looking_for_full_time: "全职" + basics_looking_for_part_time: "兼职" + basics_looking_for_remote: "远程工作" + basics_looking_for_contracting: "合同制工作" + basics_looking_for_internship: "实习" + basics_looking_for_help: "你希望找到哪种开发者职位?" + name_header: "请填写你的姓名" + name_anonymous: "匿名开发者" + name_help: "你希望雇主看到的名字,例如‘Nick Winter'。" + short_description_header: "写一段简短的自我介绍" + short_description_blurb: "在此添加一段简介让雇主一眼就发现你是否是他们正在寻找的开发者。" + short_description: "简短介绍" + short_description_help: "你是谁,你在寻求什么?请勿超过140个字符。" + skills_header: "技能" + skills_help: "按照熟练程度列出你所掌握的与开发有关的技能。" + long_description_header: "详细描述你所期望的职位" + long_description_blurb: "告诉我们你的优点与你感兴趣的职位" + long_description: "描述" + long_description_help: "向潜在的雇主描述你自己。尽量简明扼要。我们建议你列出你最感兴趣的职位。请勿超过600个字符。" work_experience: "工作经验" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" -# work_duration_help: "When did you hold this gig?" -# work_description: "Description" -# work_description_help: "What did you do there? (140 chars; optional)" + work_header: "按时间顺序列出你的工作经历" + work_years: "工作年限" + work_years_help: "你有多少年职业的(有正常收入)软件开发经验?" + work_blurb: "列出相关的工作经验,从最近的开始。" + work_employer: "雇主" + work_employer_help: "你雇主的名字。" + work_role: "职称" + work_role_help: "你的职称是什么或者说你扮演何种角色?" + work_duration: "起止时间" + work_duration_help: "你在什么时间段在职?" + work_description: "描述" + work_description_help: "你在那里主要的工作是什么?(140个字符;选填)" education: "教育程度" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." -# education_school: "School" -# education_school_help: "Name of your school." -# education_degree: "Degree" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" -# our_notes: "CodeCombat's Notes" -# remarks: "Remarks" + education_header: "列出你的教育经历。" + education_blurb: "列出你在校学习的经历。" + education_school: "学校" + education_school_help: "你就读的学校的名字。" + education_degree: "学位" + education_degree_help: "你的学位以及你学习的方向是什么?" + education_duration: "时间" + education_duration_help: "什么时候?" + education_description: "描述" + education_description_help: "说说任何与你的教育经历相关的东西。(140个字符;选填)" + our_notes: "CodeCombat的评注" + remarks: "评价" projects: "项目" -# projects_header: "Add 3 projects" -# projects_header_2: "Projects (Top 3)" -# projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" -# project_name_help: "What was the project called?" -# project_description: "Description" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" -# project_link_help: "Link to the project." -# player_code: "Player Code" + projects_header: "添加3个项目" + projects_header_2: "项目(前3个)" + projects_blurb: "展示你所参加的可以让雇主感到惊叹的项目。" + project_name: "项目名称" + project_name_help: "项目被称作什么?" + project_description: "描述" + project_description_help: "简短的介绍一下这个项目。" + project_picture: "图片" + project_picture_help: "上传一张230x115像素或更大的图片来展示这个项目。" + project_link: "链接" + project_link_help: "项目的链接。" + player_code: "玩家代码" employers: -# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." -# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." -# hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. -# get_started: "Get Started" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." + deprecation_warning_title: "抱歉,CodeCombat现不招聘雇员。" + deprecation_warning: "我们现在只专注在初学者等级的开发,暂时还不需要雇用天才级开发人员。" + hire_developers_not_credentials: "我们只招聘开发人员,不招聘证书。" # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. + get_started: "入门" + already_screened: "我们已经筛选过所有应征者了" + filter_further: ", 但是你也可以进一步筛选:" + filter_visa: "Visa" + filter_visa_yes: "US认证" + filter_visa_no: "未认证" + filter_education_top: "顶尖学校" + filter_education_other: "其他" + filter_role_web_developer: "网页开发者" + filter_role_software_developer: "软件开发者" + filter_role_mobile_developer: "移动开发者" + filter_experience: "经验" + filter_experience_senior: "上级" + filter_experience_junior: "初级" + filter_experience_recent_grad: "刚毕业" + filter_experience_student: "学院生" + filter_results: "成绩" + start_hiring: "开始招聘。" + reasons: "三个你该通过我们招聘的理由:" + everyone_looking: "每一个来这里的人是为了找寻他们的下一个机会。" + everyone_looking_blurb: "忘了那20%的LinkedIn InMain回复率。每一个被我们列入这个站点的人希望找到他们的下一个位置以及将会回应你要求自我介绍的请求。" + weeding: "坐下吧!我们已经为你将不必要的都去除了。" + weeding_blurb: "每一位被我们列举的玩家都已经通过筛选,他们都有非凡的技能。我们也通过电话进行筛选以选择应征者及记下他们的资料来节省你的时间。" + pass_screen: "他们一定会通过你的技术能力测试的!" + pass_screen_blurb: "看过他们的代码后才做出决定。有个雇主发现比起直接从Hacker News招聘,更多应征者通过他的技术能力测试。" + make_hiring_easier: "请让我的招聘变得容易。" + what: "什么是CodeCombat?" + what_blurb: "CodeCombat是一个多人浏览器编程游戏。玩家编写代码控制他们的力量来跟其他开发人员战斗。我们的玩家有技术上的各个经验。" + cost: "我们的收费多少?" + cost_blurb: "我们只收取第一年薪资的15%并在前90天提供100%退款保障。我们不对那些已经参与贵公司面试的应征者收费。" candidate_name: "姓名" candidate_location: "地点" candidate_looking_for: "寻找" @@ -1110,13 +1452,13 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese candidate_top_skills: "高级技能" candidate_years_experience: "多年工作经验" candidate_last_updated: "最后一次更新" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" + candidate_who: "谁?" + featured_developers: "主要开发者" + other_developers: "其他开发者" + inactive_developers: "不活跃的开发者" admin: -# av_espionage: "Espionage" # Really not important to translate /admin controls. + av_espionage: "Espionage" # Really not important to translate /admin controls. av_espionage_placeholder: "邮箱或用户名" av_usersearch: "用户搜索" av_usersearch_placeholder: "邮箱、用户名、姓名、任何东西" @@ -1125,12 +1467,12 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese av_entities_sub_title: "实体" av_entities_users_url: "用户" av_entities_active_instances_url: "活动实例" -# av_entities_employer_list_url: "Employer List" -# av_entities_candidates_list_url: "Candidate List" -# av_entities_user_code_problems_list_url: "User Code Problems List" + av_entities_employer_list_url: "雇主列表" + av_entities_candidates_list_url: "应征者列表" + av_entities_user_code_problems_list_url: "用户代码问题列表" av_other_sub_title: "其他" av_other_debug_base_url: "Base(用于调试 base.jade)" u_title: "用户列表" -# ucp_title: "User Code Problems" + ucp_title: "用户代码的问题" lg_title: "最新的游戏" clas: "贡献者许可协议" diff --git a/app/locale/zh-HANT.coffee b/app/locale/zh-HANT.coffee index 37412d438..d1c9907b8 100644 --- a/app/locale/zh-HANT.coffee +++ b/app/locale/zh-HANT.coffee @@ -1,35 +1,36 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese (Traditional)", translation: home: - slogan: "通過玩遊戲學習編程" + slogan: "玩遊戲學程式" no_ie: "抱歉!Internet Explorer 8 等舊的瀏覽器打不開此網站" # Warning that only shows up in IE8 and older - no_mobile: "CodeCombat 不是針對手機設備設計的,所以可能會出問題!" # Warning that shows up on mobile devices - play: "開始遊戲" # The big play button that just starts playing a level -# old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari -# old_browser_suffix: "You can try anyway, but it probably won't work." -# campaign: "Campaign" -# for_beginners: "For Beginners" -# multiplayer: "Multiplayer" # Not currently shown on home page -# for_developers: "For Developers" # Not currently shown on home page. -# or_ipad: "Or download for iPad" + no_mobile: "CodeCombat不是針對手機設備設計的,所以可能會出問題!" # Warning that shows up on mobile devices + play: "開始遊戲" # The big play button that opens up the campaign view. + old_browser: "嗯... 您的瀏覽器太老了跑不動CodeCombat,抱歉!" # Warning that shows up on really old Firefox/Chrome/Safari + old_browser_suffix: "您還是可以試試看,但它應該不能運行。" + ipad_browser: "抱歉,CodeCombat不能在iPad上的瀏覽器運行,但好消息是我們的iPad App正在等待蘋果公司審核。" + campaign: "戰役" + for_beginners: "新手專區" + multiplayer: "多人連線" # Not currently shown on home page + for_developers: "開發者專區" # Not currently shown on home page. + or_ipad: "或下載 iPad 版" nav: - play: "開始遊戲" # The top nav bar entry where players choose which levels to play -# community: "Community" + play: "返回地圖" # The top nav bar entry where players choose which levels to play + community: "社群" editor: "編輯" blog: "官方部落格" forum: "論壇" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + account: "帳號" + profile: "帳號資料" + stats: "記錄" + code: "程式碼" admin: "系統管理員" # Only shows up when you are an admin home: "首頁" contribute: "貢獻" legal: "版權聲明" about: "關於" contact: "聯繫我們" - twitter_follow: "在Twitter關注" -# teachers: "Teachers" + twitter_follow: "在 Twitter 關注" + teachers: "教師" modal: close: "關閉" @@ -41,408 +42,651 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese diplomat_suggestion: title: "幫我們翻譯CodeCombat" # This shows up when a player switches to a non-English language using the language selector. sub_heading: "我們需要您的語言技能" - pitch_body: "我們開發了CodeCombat的英文版,但是現在我們的玩家遍佈全球。很多人想玩中文版的,卻不會說英文,所以如果你中英文都會,請考慮一下參加我們的翻譯工作,幫忙把 CodeCombat 網站還有所有的關卡翻譯成中文(繁体)。" + pitch_body: "我們開發了CodeCombat的英文版,但是現在我們的玩家遍佈全球。很多人想玩中文版的,卻不會說英文,所以如果您中英文都會,請考慮一下參加我們的翻譯工作,幫忙把 CodeCombat 網站還有所有的關卡翻譯成中文(繁体)。" missing_translations: "直至所有正體中文的翻譯完畢,當無法提供正體中文時還會以英文顯示。" learn_more: "關於成為外交官" subscribe_as_diplomat: "註冊成為外交官" play: -# play_as: "Play As" # Ladder page -# spectate: "Spectate" # Ladder page -# players: "players" # Hover over a level on /play -# hours_played: "hours played" # Hover over a level on /play -# items: "Items" # Tooltip on item shop button from /play -# unlock: "Unlock" # For purchasing items and heroes -# confirm: "Confirm" -# owned: "Owned" # For items you own -# locked: "Locked" -# available: "Available" -# skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play -# achievements: "Achievements" # Tooltip on achievement list button from /play -# account: "Account" # Tooltip on account button from /play -# settings: "Settings" # Tooltip on settings button from /play -# next: "Next" # Go from choose hero to choose inventory before playing a level -# change_hero: "Change Hero" # Go back from choose inventory to choose hero -# choose_inventory: "Equip Items" -# buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + play_as: "扮演" # Ladder page + spectate: "旁觀" # Ladder page + players: "玩家" # Hover over a level on /play + hours_played: "hours played" # Hover over a level on /play + items: "物品" # Tooltip on item shop button from /play + unlock: "解鎖" # For purchasing items and heroes + confirm: "確認" + owned: "擁有" # For items you own + locked: "鎖定" + purchasable: "可購買" # For a hero you unlocked but haven't purchased + available: "啟用" + skills_granted: "將習得技能" # Property documentation details + heroes: "英雄" # Tooltip on hero shop button from /play + achievements: "成就" # Tooltip on achievement list button from /play + account: "帳號" # Tooltip on account button from /play + settings: "設定" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play + next: "下一步" # Go from choose hero to choose inventory before playing a level + change_hero: "更換英雄" # Go back from choose inventory to choose hero + choose_inventory: "裝備物品" + buy_gems: "購買鑽石" + subscription_required: "需要訂購" + anonymous: "匿名玩家" level_difficulty: "難度" campaign_beginner: "新手指南" -# awaiting_levels_adventurer_prefix: "We release five levels per week." -# awaiting_levels_adventurer: "Sign up as an Adventurer" -# awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "選取關卡" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "你可以選擇以下任意關卡,或者討論以上的關卡 " - adventurer_forum: "冒險家論壇" - adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "...在這裡可以學到編程技巧。" - campaign_dev: "隨機關卡" - campaign_dev_description: "...在這裡你可以學到做一些較複雜的程式技巧。" + awaiting_levels_adventurer_prefix: "我們每周將釋出五個等級。" # {change} + awaiting_levels_adventurer: "註冊成為冒險家" + awaiting_levels_adventurer_suffix: "成為第一個挑戰新關卡的冒險家吧!" + adjust_volume: "調整音量" campaign_multiplayer: "多人競技場" - campaign_multiplayer_description: "...在這裡你可以和其他玩家進行對戰。" - campaign_player_created: "玩家建立的關卡" - campaign_player_created_description: "...挑戰同伴的創意 <a href=\"/contribute#artisan\">技術指導</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" + campaign_multiplayer_description: "...在這裡您可以和其他玩家進行對戰。" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + + share_progress_modal: + blurb: "您正在建立優秀的進度! 告訴別人您已經從CodeCombat學習到多少東西." # {change} + email_invalid: "郵件地址無效." + form_blurb: "在底下輸入他們的郵件並且我們將秀給他們!" + form_label: "郵件地址" + placeholder: "郵件地址" + title: "出色的作品, 學徒" login: sign_up: "註冊" log_in: "登入" -# logging_in: "Logging In" + logging_in: "登入中" log_out: "登出" - recover: "找回帳號" -# authenticate_gplus: "Authenticate G+" -# load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" -# finishing: "Finishing" + forgot_password: "忘記密碼?" + authenticate_gplus: "以 Google+ 帳號登入" + load_profile: "讀取 Google+ 個人簡介" + finishing: "結束" + sign_in_with_facebook: "使用 Facebook 登入" + sign_in_with_gplus: "使用 Google+ 登入" + signup_switch: "建立一個帳號" signup: - create_account_title: "建立帳號儲存進度" - description: "登入以儲存遊戲進度:" email_announcements: "通過郵件接收通知" - coppa: "13歲以上或非美國公民" - coppa_why: "爲什麽?" creating: "帳號建立中..." sign_up: "註冊" log_in: "登入" -# social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." + social_signup: "您也可以使用G+或Facebook帳號註冊:" + required: "在這麼做之前必須先登入。" + login_switch: "已經有申請帳號了嗎?" recover: recover_account_title: "復原帳號" send_password: "送出新密碼" -# recovery_sent: "Recovery email sent." + recovery_sent: "密碼重置的信件已寄出." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "攻擊" + secondary: "防禦" + armor: "裝甲" + accessories: "飾品" + misc: "輔助" + books: "書籍" common: - loading: "Loading..." + back: "向後瀏覽" # When used as an action verb, like "Navigate backward" + continue: "繼續前進" # When used as an action verb, like "Continue forward" + loading: "載入中..." saving: "儲存中..." sending: "發送中...." -# send: "Send" + send: "送出" cancel: "取消" save: "存檔" -# publish: "Publish" -# create: "Create" - manual: "手動發動" - fork: "Fork" - play: "播放" # When used as an action verb, like "Play next level" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + publish: "發佈" + create: "創造" + manual: "手動" + fork: "分支" + play: "進入戰役" # When used as an action verb, like "Play next level" + retry: "重試" + actions: "行為" + info: "介紹" + help: "求助" + watch: "關注" + unwatch: "取消關注" + submit_patch: "送出修補" + submit_changes: "送出修改" +# save_changes: "Save Changes" general: -# and: "and" + and: "和" name: "名字" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" + date: "日期" + body: "正文" + version: "版本" + pending: "處理中" + accepted: "已接受" + rejected: "未接受" + withdrawn: "撤回" + submitter: "提交者" + submitted: "已提交" + commit_msg: "送出訊息" + review: "回顧" + version_history: "版本歷史" + version_history_for: "版本歷史: " + select_changes: "選擇下面兩項修改來觀察不同" + undo_prefix: "取消" + undo_shortcut: "(Ctrl+Z)" + redo_prefix: "重做" + redo_shortcut: "(Ctrl+Shift+Z)" + play_preview: "播放預覽本關卡" + result: "結果" + results: "結果" + description: "描述" or: "或" -# subject: "Subject" -# email: "Email" -# password: "Password" + subject: "主題" + email: "郵件" + password: "密碼" message: "訊息" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" -# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard + code: "代碼" + ladder: "升級比賽" + when: "當" + opponent: "對手" + rank: "階級" + score: "分數" + win: "獲勝" + loss: "失敗" + tie: "平手" + easy: "簡單" + medium: "中等" + hard: "困難" + player: "玩家" + player_level: "等級" # Like player level 5, not like level: Dungeons of Kithgard + warrior: "武士" + ranger: "搜索兵" + wizard: "巫師" -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + units: + second: "秒" + seconds: "秒" + minute: "分鐘" + minutes: "分鐘" + hour: "小時" + hours: "小時" + day: "日" + days: "日" + week: "周" + weeks: "周" + month: "個月" + months: "個月" + year: "年" + years: "年" play_level: done: "完成" home: "首頁" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" -# skip: "Skip" -# game_menu: "Game Menu" + level: "關卡" # Like "Level: Dungeons of Kithgard" + skip: "跳過" + game_menu: "遊戲選單" guide: "指南" restart: "重新開始" goals: "目標" -# goal: "Goal" -# running: "Running..." -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" + goal: "目標" + running: "執行中..." + success: "成功!" + incomplete: "未完成" + timed_out: "時間用盡" + failing: "失敗" action_timeline: "行動時間軸" click_to_select: "點擊選擇一個單元。" -# control_bar_multiplayer: "Multiplayer" -# control_bar_join_game: "Join Game" -# reload: "Reload" + control_bar_multiplayer: "多人遊戲" + control_bar_join_game: "加入遊戲" + reload: "重新載入" reload_title: "重新載入程式碼?" reload_really: "確定重設所有的程式碼?" reload_confirm: "重設所有程式碼" -# victory_title_prefix: "" + victory: "勝利" + victory_title_prefix: "" victory_title_suffix: " 完成" victory_sign_up: "保存進度" - victory_sign_up_poke: "想保存你的程式碼?建立一個免費帳號吧!" + victory_sign_up_poke: "想保存您的程式碼?建立一個免費帳號吧!" victory_rate_the_level: "評估關卡: " # Only in old-style levels. -# victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "下一關" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" -# victory_saving_progress: "Saving Progress" + victory_return_to_ladder: "返回升級比賽模式" + victory_play_continue: "繼續" + victory_saving_progress: "儲存進度" victory_go_home: "返回首頁" # Only in old-style levels. victory_review: "給我們回饋!" # Only in old-style levels. - victory_hour_of_code_done: "你完成了嗎?" + victory_hour_of_code_done: "您完成了嗎?" victory_hour_of_code_done_yes: "是的,我完成了我的程式碼!" + victory_experience_gained: "取得經驗值" + victory_gems_gained: "取得寶石" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "指南" tome_minion_spells: "助手的咒語" # Only in old-style levels. tome_read_only_spells: "唯讀的咒語" # Only in old-style levels. tome_other_units: "其他單位" # Only in old-style levels. -# tome_cast_button_run: "Run" -# tome_cast_button_running: "Running" -# tome_cast_button_ran: "Ran" -# tome_submit_button: "Submit" -# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. -# tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). + tome_cast_button_run: "運作" + tome_cast_button_running: "運作中" + tome_cast_button_ran: "已運作" + tome_submit_button: "送出" + tome_reload_method: "重新載入該方法的原代碼" # Title text for individual method reload button. + tome_select_method: "選擇一個方法" + tome_see_all_methods: "查看所有可編輯的方法" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "選擇一個人物來施放" tome_available_spells: "可用的法術" -# tome_your_skills: "Your Skills" -# tome_current_method: "Current Method" -# hud_continue_short: "Continue" -# code_saved: "Code Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." -# tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." -# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "自定義巫師" + tome_your_skills: "您的技能" + tome_help: "幫助" + tome_current_method: "現在的方法" + hud_continue_short: "繼續" + code_saved: "程式碼已保存" + skip_tutorial: "跳過 (esc)" + keyboard_shortcuts: "快捷鍵" + loading_ready: "準備!" + loading_start: "開始戰役" + problem_alert_title: "修正您的程式碼" + problem_alert_help: "導引" + time_current: "現在:" + time_total: "最大值:" + time_goto: "前往:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" + infinite_loop_try_again: "再試一次" + infinite_loop_reset_level: "重置關卡" + infinite_loop_comment_out: "在我的程式碼中加入注解" + tip_toggle_play: "使用 Ctrl+P 切換 播放/暫停." + tip_scrub_shortcut: "Ctrl+[ 快退; Ctrl+] 快進." # {change} + tip_guide_exists: "點擊頁面上方的指南,可獲得更多有用的訊息." + tip_open_source: "「CodeCombat」100% 開源!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" + tip_beta_launch: "「CodeCombat」在2013年10月進入 BETA 測試。" + tip_think_solution: "思考解決方法而不是問題." + tip_theory_practice: "理論上, 理論和實作之間是沒有區別. 但是實作上, 這兩者是有區別的. - Yogi Berra" + tip_error_free: "有兩種方式可以寫出沒有錯誤的程式; 但只有第三種可以達到預期效果. - Alan Perlis" + tip_debugging_program: "如果除錯是一種清除錯誤的過程, 那麼編寫程式就是一種放置錯誤的過程. - Edsger W. Dijkstra" + tip_forums: "前往論壇並且告訴我們您所思考的!" + tip_baby_coders: "在未來, 就算小孩也能成為大法師." + tip_morale_improves: "直到士氣提升之前,載入的動作將持續." + tip_all_species: "我們相信所有民族都有平等的機會學習編寫程式" + tip_reticulating: "網格狀鋸齒(指卡頓現象)" + tip_harry: "巫師, " + tip_great_responsibility: "隨著擁有強大的編程技巧,除錯的責任將越大." + tip_munchkin: "如果您不吃掉您的蔬菜,那小矮人將在您沈睡時找到您." + tip_binary: "只有2種人在這世上: 那些懂2進位的,和哪些不懂得." + tip_commitment_yoda: "一個程序員必須擁有強烈的責任感和一顆認真的心. ~ Yoda" + tip_no_try: "做,或者不做。這邊不存在嘗試的選項. - Yoda" + tip_patience: "年輕的學徒,您必須擁有耐心. - Yoda" + tip_documented_bug: "一個寫在文件裡的錯誤不是錯誤;它是功能." + tip_impossible: "事情總是看起來不可能直到它成真那刻. - Nelson Mandela" + tip_talk_is_cheap: "多說無益,公開您的代碼. - Linus Torvalds" + tip_first_language: "您經歷的第一門程式語言是最慘痛的事情. - Alan Kay" + tip_hardware_problem: "Q: 換一顆燈泡需要多少位程序員? A: 一位也不用, 它是個硬體問題." + tip_hofstadters_law: "Hofstadter 定律: 完成一件複雜的事花費的時間總是超乎預期, 甚至您早已知道這個現象( Hofstadter 定律)." + tip_premature_optimization: "過早的優化是萬惡之源. - Donald Knuth" + tip_brute_force: "當您游移不定時, 就是用暴力解. - Ken Thompson" + tip_extrapolation: "只有 2 種人在這世上: 一種人是能夠根據不完整資訊而推斷..." + tip_superpower: "編程讓我們最接近擁有超能力." + tip_control_destiny: "在真正的開源, 您有權利控制自己的命運. - Linus Torvalds" + tip_no_code: "在速度上,沒有任何代碼可以超過無代碼" + tip_code_never_lies: "代碼從不說謊, 但註釋偶爾會. — Ron Jeffries" + tip_reusable_software: "在軟體被重複使用前,它必須能用." + tip_optimization_operator: "每種語言都有一個優化的運算符號. 對大部份而言,那符號是‘//’" + tip_lines_of_code: "使用代碼行數來管理開發進度就好像使用秤重器來管理建造飛機. — Bill Gates" + tip_source_code: "我想改變世界但他們不給我源代碼" + tip_javascript_java: "Java 和 JavaScript 的關係就好比馬和馬雲. - Chris Heilmann" + tip_move_forward: "不論您做啥, 持續前進. - Martin Luther King Jr." + tip_google: "擁有一個您不能解決的問題? Google 它!" + tip_adding_evil: "增加一個邪惡之捏." + tip_hate_computers: "關於自我覺得恨透電腦的那群人. 其實他們真正應該恨的事情是糟糕的程序員. - Larry Niven" + tip_open_source_contribute: "你可以幫助「CodeCombat」提高!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: -# inventory_tab: "Inventory" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" + inventory_tab: "倉庫" + save_load_tab: "保存/載入" + options_tab: "選項" + guide_tab: "導引" + guide_video_tutorial: "影片教學" + guide_tips: "提示" multiplayer_tab: "多人遊戲" -# auth_tab: "Sign Up" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" -# auth_caption: "Save your progress." + auth_tab: "註冊" + inventory_caption: "裝備您的英雄" + choose_hero_caption: "選擇英雄, 語言" + save_load_caption: "... 觀看歷史紀錄" + options_caption: "設置設定" + guide_caption: "文件與小撇步" + multiplayer_caption: "跟朋友一起玩!" + auth_caption: "儲存進度." -# inventory: -# choose_inventory: "Equip Items" -# equipped_item: "Equipped" -# available_item: "Available" -# restricted_title: "Restricted" -# should_equip: "(double-click to equip)" -# equipped: "(equipped)" -# locked: "(locked)" -# restricted: "(restricted in this level)" -# equip: "Equip" -# unequip: "Unequip" + leaderboard: + leaderboard: "排行榜" + view_other_solutions: "查看其他解法" # {change} + scores: "分數" + top_players: "頂級玩家由" + day: "今天" + week: "這周" + all: "長期以來" + time: "時間" + damage_taken: "遭受的攻擊" + damage_dealt: "造成的攻擊" + difficulty: "困難度" + gold_collected: "收集的黃金" -# buy_gems: -# few_gems: "A few gems" -# pile_gems: "Pile of gems" -# chest_gems: "Chest of gems" -# purchasing: "Purchasing..." -# declined: "Your card was declined" -# retrying: "Server error, retrying." + inventory: + choose_inventory: "裝備物品" + equipped_item: "已裝備" + required_purchase_title: "需要的" + available_item: "可使用" + restricted_title: "已限制" + should_equip: "連點物品兩下可裝備" + equipped: "(已裝備)" + locked: "(已鎖定)" + restricted: "(受本關卡限制)" + equip: "裝備" + unequip: "脫下" -# choose_hero: -# choose_hero: "Choose Your Hero" -# programming_language: "Programming Language" -# programming_language_description: "Which programming language do you want to use?" -# default: "Default" -# experimental: "Experimental" -# python_blurb: "Simple yet powerful, great for beginners and experts." -# javascript_blurb: "The language of the web. (Not the same as Java.)" -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." -# status: "Status" -# weapons: "Weapons" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" -# attack: "Damage" # Can also translate as "Attack" -# health: "Health" -# speed: "Speed" -# regeneration: "Regeneration" -# range: "Range" # As in "attack or visual range" -# blocks: "Blocks" # As in "this shield blocks this much damage" -# skills: "Skills" + buy_gems: + few_gems: "一些寶石" + pile_gems: "一堆寶石" + chest_gems: "一箱寶石" + purchasing: "購買中..." + declined: "您的信用卡被拒絕" + retrying: "伺服器錯誤, 正在重試." + prompt_title: "寶石不足" + prompt_body: "想要取得更多?" + prompt_button: "進入商店" + recovered: "先前購買的寶石已回復. 請重新載入頁面." +# price: "x3500 / mo" -# skill_docs: -# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this -# read_only: "read-only" -# action_name: "name" -# action_cooldown: "Takes" -# action_specific_cooldown: "Cooldown" -# action_damage: "Damage" -# action_range: "Range" -# action_radius: "Radius" -# action_duration: "Duration" -# example: "Example" -# ex: "ex" # Abbreviation of "example" -# current_value: "Current Value" -# default_value: "Default value" -# parameters: "Parameters" -# returns: "Returns" -# granted_by: "Granted by" + subscribe: + comparison_blurb: "訂閱 CodeCombat 來磨練您的技巧!" + feature1: "60 個以上的基本關卡散佈在4張地圖中" # {change} + feature2: "7 個強壯的<strong>新英雄</strong>並每隻都有不同技巧!" # {change} + feature3: "30 個以上的額外關卡" # {change} + feature4: "每個月<strong>3500顆額外寶石</strong>!" + feature5: "視頻教學" + feature6: "頂級信箱支援" +# feature7: "Private <strong>Clans</strong>" + free: "免費" + month: "月" + subscribe_title: "訂閱" + unsubscribe: "取消訂閱" + confirm_unsubscribe: "確認訂閱" + never_mind: "沒關係,我仍然愛您" + thank_you_months_prefix: "感謝您這幾個" + thank_you_months_suffix: "月來的支持" + thank_you: "感謝您支持CodeCombat." + sorry_to_see_you_go: "捨不得您離開! 請讓我們知道我們如何做得更好." + unsubscribe_feedback_placeholder: "我們做錯事了嗎?" + parent_button: "詢問您的父母" + parent_email_description: "我們將寄信向他們說明,所以他們能放心幫您訂閱 CodeCombat." + parent_email_input_invalid: "信箱位址無效." + parent_email_input_label: "父母信箱位址" + parent_email_input_placeholder: "輸入父母信箱" + parent_email_send: "寄信" + parent_email_sent: "已寄信!" + parent_email_title: "您父母信箱是?" + parents: "致家長" + parents_title: "您的孩子將學習編寫程式." # {change} + parents_blurb1: "使用 CodeCombat , 您的孩子學習真正的編寫程式. 他們學習從簡單的指令,漸進到更加進階的課題." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" + parents_blurb2: "每月支付 $9.99 美金, 他們每週獲得新挑戰以及使用信件取得專業程式員的幫助." # {change} + parents_blurb3: "沒有風險: 保證 100% 退費, 一步取消訂閱." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" + stripe_description: "每月訂閱" + subscription_required_to_play: "您將需要訂閱來開啟這關." + unlock_help_videos: "訂閱來解開所有鎖住得教學影片." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" + choose_hero: + choose_hero: "選擇您的英雄" + programming_language: "程式語言" + programming_language_description: "您要使用哪個程式語言?" + default: "預設" + experimental: "測試中" + python_blurb: "簡單且強大,適合新手跟專家。" + javascript_blurb: "網頁程式語言。(不是 Java 喔)" + coffeescript_blurb: "懶人版的 Javascript。" + clojure_blurb: "Lisp 進化版。" + lua_blurb: "許多遊戲引擎支援的程式語言。" + io_blurb: "簡單但不有名。" + status: "狀態" + hero_type: "种类" + weapons: "武器" + weapons_warrior: "刀劍 - 短距離、非魔法" + weapons_ranger: "十字弓, 槍砲 - 長距離、非魔法" + weapons_wizard: "魔杖, 法杖 - 長距離、魔法" + attack: "傷害" # Can also translate as "Attack" + health: "血量" + speed: "速度" + regeneration: "再生" + range: "距離" # As in "attack or visual range" + blocks: "格擋" # As in "this shield blocks this much damage" + backstab: "背刺" # As in "this dagger does this much backstab damage" + skills: "技能" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." + available_for_purchase: "可以購買" # Shows up when you have unlocked, but not purchased, a hero in the hero store + level_to_unlock: "解鎖關卡:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) + restricted_to_certain_heroes: "特定英雄才可遊玩此關卡。" -# options: -# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." -# editor_config: "Editor Config" -# editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" -# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." + skill_docs: + writable: "可編輯" # Hover over "attack" in Your Skills while playing a level to see most of this + read_only: "唯讀" + action_name: "名稱" + action_cooldown: "釋放時間" + action_specific_cooldown: "冷卻時間" + action_damage: "傷害" + action_range: "距離" + action_radius: "範圍" + action_duration: "持續" + example: "範例" + ex: "ex" # Abbreviation of "example" + current_value: "現在數值" + default_value: "預設數值" + parameters: "參數" + returns: "回傳" + granted_by: "賦予" + + save_load: + granularity_saved_games: "已儲存" + granularity_change_history: "歷史紀錄" + + options: + general_options: "一般設定" # Check out the Options tab in the Game Menu while playing a level + volume_label: "音量" + music_label: "音樂" + music_description: "開關背景音樂。" + editor_config: "設定編輯器" + editor_config_title: "編輯器設定值" + editor_config_level_language_label: "本關卡使用的語言" + editor_config_level_language_description: "定義此關卡使用哪個程式語言。" + editor_config_default_language_label: "預設程式語言" + editor_config_default_language_description: "定義此關卡預設使用哪個程式語言。" + editor_config_keybindings_label: "快捷鍵綁定" + editor_config_keybindings_default: "預設 (Ace編輯器)" + editor_config_keybindings_description: "從一般編輯器額外加入快捷鍵功能。" + editor_config_livecompletion_label: "語法自動校正" + editor_config_livecompletion_description: "當編寫語法的時候自動提示。" + editor_config_invisibles_label: "顯示隱藏字元" + editor_config_invisibles_description: "顯示如空白鍵或TAB鍵等隱藏字元。" + editor_config_indentguides_label: "顯示縮排導引" + editor_config_indentguides_description: "顯示縮排行數以便閱讀。" + editor_config_behaviors_label: "智慧校正" + editor_config_behaviors_description: "自動填入小括號、大括號以及引號。" about: why_codecombat: "為什麼使用CodeCombat?" - why_paragraph_1: "想學程式嗎? 你不需要課程。你需要的只是大量的時間去\"玩\"程式。" + why_paragraph_1: "想學程式嗎? 您不需要課程。您需要的只是大量的時間去\"玩\"程式。" why_paragraph_2_prefix: "寫程式應該是有趣的。當然不是" why_paragraph_2_italic: "「耶!拿到獎章了。」" why_paragraph_2_center: "的有趣, 而是" why_paragraph_2_italic_caps: "「媽我不要出去玩,我要寫完這段!」" - why_paragraph_2_suffix: "般引人入勝。這是為甚麼CodeCombat被設計成多人對戰「遊戲」,而不是遊戲化「課程」。在你對這遊戲無法自拔之前,我們是不會放棄的─幫然,這個遊戲,將是有益於你的。" - why_paragraph_3: "如果你要沉迷遊戲的話,就來沉迷CodeCombat,成為科技時代的魔法師吧!" -# press_title: "Bloggers/Press" -# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" -# press_paragraph_1_link: "press packet" -# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." -# team: "Team" -# george_title: "CEO" -# george_blurb: "Businesser" -# scott_title: "Programmer" -# scott_blurb: "Reasonable One" -# nick_title: "Programmer" -# nick_blurb: "Motivation Guru" -# michael_title: "Programmer" -# michael_blurb: "Sys Admin" -# matt_title: "Programmer" -# matt_blurb: "Bicyclist" + why_paragraph_2_suffix: "般引人入勝。這是為甚麼CodeCombat被設計成多人對戰「遊戲」,而不是遊戲化「課程」。在您對這遊戲無法自拔之前,我們是不會放棄的─幫然,這個遊戲,將是有益於您的。" + why_paragraph_3: "如果您要沉迷遊戲的話,就來沉迷CodeCombat,成為科技時代的魔法師吧!" + press_title: "部落格/新聞稿" + press_paragraph_1_prefix: "想要撰寫有關我們的文章?請自由取用或下載我們的" + press_paragraph_1_link: "新聞稿懶人包" + press_paragraph_1_suffix: ",裡面所有的LOGO和圖片都可以使用,並且不必另外知會我們。" + team: "製作團隊" + george_title: "CEO" # {change} + george_blurb: "商人" + scott_title: "程式員" # {change} + scott_blurb: "理性至上" + nick_title: "程式員" # {change} + nick_blurb: "亢奮的Guru" + michael_title: "程式員" + michael_blurb: "系統管理員" + matt_title: "程式員" # {change} + matt_blurb: "競速單車玩家" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" -# versions: -# save_version_title: "Save New Version" -# new_major_version: "New Major Version" -# cla_prefix: "To save changes, first you must agree to our" -# cla_url: "CLA" -# cla_suffix: "." -# cla_agree: "I AGREE" + teachers: + title: "CodeCombat致教師的說明" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." + sys_requirements_title: "系統要求" + sys_requirements_1: "因為CodeCombat是遊戲, 相比播放影片它讓電腦花費更多資源去順暢的執行. 為了讓所有人都可以接觸,我們已經讓它在現在的瀏覽器和老舊的電腦上執行最佳化. 以下是我們為了讓您順暢體驗Hour of Code所給的系統建議:" # {change} + sys_requirements_2: "使用較新的Chrome or Firefox版本." # {change} + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." + + versions: + save_version_title: "保存新版本" + new_major_version: "新的重要版本" + submitting_patch: "正在提交修補..." + cla_prefix: "想保存修改, 您必須先保存我們的" + cla_url: "貢獻者許可協議" + cla_suffix: "。" + cla_agree: "我同意" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "聯繫我們" - welcome: "很高興收到你的信!用這個表格給我們發電郵。 " - contribute_prefix: "如果你想貢獻程式,請看 " - contribute_page: "程式貢獻頁面" - contribute_suffix: "!" + welcome: "很高興收到您的信!用這個表格給我們發電郵。 " forum_prefix: "如果有任何問題, 請至" forum_page: "論壇" forum_suffix: "討論。" + faq_prefix: "這裡還有一個" + faq: "FAQ" + subscribe_prefix: "如果您需要幫助來解決關卡, 請" + subscribe: "訂閱CodeCombat" + subscribe_suffix: "並且我們樂意提供代碼相關的協助." + subscriber_support: "您已經是個CodeCombat訂閱者, 我們將提供優先的協助." + screenshot_included: "包含螢幕截圖." + where_reply: "我們回覆到?" send: "意見反饋" -# contact_candidate: "Contact Candidate" # Deprecated -# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated + contact_candidate: "聯繫候選人員" # Deprecated + recruitment_reminder: "使用這張表格來聯繫您有興趣的求職者. 記得CodeCombat將收取員工第一年薪水的15%當作佣金. 佣金須在僱用時就必須付清並且之後的90天內如果員工離職則可退款. 兼職, 遠端工作, 契約員工和實習生都可免除費用." # Deprecated account_settings: title: "帳號設定" @@ -450,23 +694,30 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese autosave: "自動保存修改" me_tab: "我" picture_tab: "頭像" -# upload_picture: "Upload a picture" + delete_account_tab: "刪除您的帳戶" + wrong_email: "錯誤的Email" +# wrong_password: "Wrong Password" + upload_picture: "上傳圖片" + delete_this_account: "永久性的刪除帳戶" + god_mode: "上帝模式" password_tab: "密碼" emails_tab: "郵件" -# admin: "Admin" + admin: "管理員" new_password: "新密碼" new_password_verify: "確認密碼" + type_in_email: "輸入您的Email來確認刪除" # {change} +# type_in_password: "Also, type in your password." email_subscriptions: "訂閱" -# email_subscriptions_none: "No Email Subscriptions." + email_subscriptions_none: "無Email訂閱" email_announcements: "通知" - email_announcements_description: "接收關於 CodeCombat 的新聞和開發消息。" -# email_notifications: "Notifications" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." + email_announcements_description: "接收關於CodeCombat的新聞和開發消息。" + email_notifications: "通知" + email_notifications_summary: "您在CodeCombat的活動,會透過Email自動通知,您可選擇開啟或關閉。" + email_any_notes: "任何通知" + email_any_notes_description: "停用後您將不會收到來自CodeCombat的任何通知。" + email_news: "新聞" + email_recruit_notes: "工作機會" + email_recruit_notes_description: "如果您玩得不錯,我們或許會考慮給您一份工作。" contributor_emails: "貢獻者電郵" contribute_prefix: "我們在尋找志同道合的人!請到 " contribute_page: "貢獻頁面" @@ -475,282 +726,337 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese error_saving: "保存時發生錯誤" saved: "修改已儲存" password_mismatch: "密碼不正確。" -# password_repeat: "Please repeat your password." -# job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated -# job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." -# job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - wizard_tab: "巫師" - wizard_color: "巫師 衣服 顏色" + password_repeat: "請重複輸入您的密碼。" + job_profile: "工作經歷" # Rest of this section (the job profile stuff and wizard stuff) is deprecated + job_profile_approved: "您的工作經歷已被CodeCombat驗證. 僱主將可以隨意瀏覽直到您設定為不啟動狀態或者已經四周沒有改變" + job_profile_explanation: "Hi! 請填寫下列資訊, 我們將使用它幫您媒合一份開發工作." + sample_profile: "觀看範例基本資料" + view_profile: "瀏覽您的基本資料" -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# run_code: "Run current code." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." + keyboard_shortcuts: + keyboard_shortcuts: "鍵盤快捷鍵" + space: "Space" + enter: "Enter" +# press_enter: "press enter" + escape: "Esc" + shift: "Shift" + run_code: "執行當前腳本" + run_real_time: "立即執行" + continue_script: "使用最後一次使用的腳本" + skip_scripts: "略過之前所有腳本" + toggle_playback: "執行/暫停開關" + scrub_playback: "向前/向後移動一段時間" + single_scrub_playback: "向前/向後移動一個畫面" + scrub_execution: "向前/向後移動一句語句" + toggle_debug: "顯示/關閉除錯訊息" + toggle_grid: "顯示/關閉網格提示" + toggle_pathfinding: "顯示/關閉路徑尋找提示" + beautify: "利用標準格式來美化您的代碼" + maximize_editor: "最大化/最小化代碼編輯器" -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# social_blog: "Read the CodeCombat blog on Sett" -# social_discource: "Join the discussion on our Discourse forum" -# social_facebook: "Like CodeCombat on Facebook" -# social_twitter: "Follow CodeCombat on Twitter" -# social_gplus: "Join CodeCombat on Google+" -# social_hipchat: "Chat with us in the public CodeCombat HipChat room" -# contribute_to_the_project: "Contribute to the project" + community: + main_title: "CodeCombat社群" + introduction: "查看您可能可以參與的項目以及選擇最吸引您的項目. 我們期待與您一起工作!" + level_editor_prefix: "使用CodeCombat" + level_editor_suffix: "來創造和編輯關卡. 許多人已經創造關卡用在課堂或黑客松,或者給予朋友或兄弟姐妹. 如果您覺得創建一個全新的關卡非常困難,您可以先從現成的開始做起!" + thang_editor_prefix: "我們稱呼遊戲中的單位叫'thangs'. 使用" + thang_editor_suffix: "來修改CodeCombat的原材料. 讓遊戲中的東西可以扔砲彈, 修改遊戲動畫的方向, 調整單位的生命值, 或者上傳自製的素材." + article_editor_prefix: "看到有錯誤在我們的文件中嗎? 想要自己設計指令嗎? 查看我們的" + article_editor_suffix: "以及幫助CodeCombat玩家獲得更多知識在遊戲中." + find_us: "通過這些網站連繫我們" + social_blog: "閱讀CodeCombat在Sett上的部落格" + social_discource: "加入我們在Discourse論壇上的討論" + social_facebook: "關注CodeCombat的Facebook" + social_twitter: "關注CodeCombat的Twitter" + social_gplus: "關注CodeCombat的Google+" + social_hipchat: "通過公共的HipChat與我們交流" + contribute_to_the_project: "貢獻這專案" -# classes: -# archmage_title: "Archmage" -# archmage_title_description: "(Coder)" -# artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" -# scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" -# diplomat_title_description: "(Translator)" -# ambassador_title: "Ambassador" -# ambassador_title_description: "(Support)" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" -# editor: -# main_title: "CodeCombat Editors" -# article_title: "Article Editor" -# thang_title: "Thang Editor" -# level_title: "Level Editor" -# achievement_title: "Achievement Editor" -# back: "Back" -# revert: "Revert" -# revert_models: "Revert Models" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." -# generate_terrain: "Generate Terrain" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" -# level_some_options: "Some Options?" -# level_tab_thangs: "Thangs" -# level_tab_scripts: "Scripts" -# level_tab_settings: "Settings" -# level_tab_components: "Components" -# level_tab_systems: "Systems" -# level_tab_docs: "Documentation" -# level_tab_thangs_title: "Current Thangs" -# level_tab_thangs_all: "All" -# level_tab_thangs_conditions: "Starting Conditions" -# level_tab_thangs_add: "Add Thangs" -# delete: "Delete" -# duplicate: "Duplicate" -# rotate: "Rotate" -# level_settings_title: "Settings" -# level_component_tab_title: "Current Components" -# level_component_btn_new: "Create New Component" -# level_systems_tab_title: "Current Systems" -# level_systems_btn_new: "Create New System" -# level_systems_btn_add: "Add System" -# level_components_title: "Back to All Thangs" -# level_components_type: "Type" -# level_component_edit_title: "Edit Component" -# level_component_config_schema: "Config Schema" -# level_component_settings: "Settings" -# level_system_edit_title: "Edit System" -# create_system_title: "Create New System" -# new_component_title: "Create New Component" -# new_component_field_system: "System" -# new_article_title: "Create a New Article" -# new_thang_title: "Create a New Thang Type" -# new_level_title: "Create a New Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" -# article_search_title: "Search Articles Here" -# thang_search_title: "Search Thang Types Here" -# level_search_title: "Search Levels Here" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" -# pop_i18n: "Populate I18N" + classes: + archmage_title: "大法師" + archmage_title_description: "(Coder)" + archmage_summary: "如果您是個在coding教育遊戲有興趣的開發者, 成為大法師來幫助我們建立CodeCombat!" + artisan_title: "工匠" + artisan_title_description: "(Level Builder)" + artisan_summary: "建造遊戲關卡並且分享給您的朋友們. 成為工匠來幫助其他人學習編程." + adventurer_title: "冒險家" + adventurer_title_description: "(Level Playtester)" + adventurer_summary: "提前一周免費取得我們新的關卡(甚至是訂閱的內容)並且提前在釋出前幫助我們找出錯誤." + scribe_title: "文書" + scribe_title_description: "(Article Editor)" + scribe_summary: "好的程式需要好的文件. 來自全世界數百萬的玩家一起編寫, 編輯和提升文件的可讀性." + diplomat_title: "外交官" + diplomat_title_description: "(Translator)" + diplomat_summary: "借由我們的外交官,CodeCombat已翻譯到45種以上的語言. 幫助我們並且貢獻翻譯." + ambassador_title: "使節" + ambassador_title_description: "(Support)" + ambassador_summary: "安撫我們論壇的用戶並且提供發問者適當的方向. 我們的使節代表CodeCombat面對全世界." -# article: -# edit_btn_preview: "Preview" -# edit_article_title: "Edit Article" + editor: + main_title: "CodeCombat編輯器" + article_title: "文章編輯器" + thang_title: "物品編輯器" + level_title: "關卡編輯器" + achievement_title: "目標編輯器" + poll_title: "投票編輯器" + back: "後退" + revert: "還原" + revert_models: "還原模式" + pick_a_terrain: "選擇地形" + dungeon: "地牢" + indoor: "室內" + desert: "沙漠" + grassy: "草地" +# mountain: "Mountain" +# glacier: "Glacier" + small: "小的" + large: "大的" + fork_title: "產生新分支" + fork_creating: "產生分支中..." + generate_terrain: "產生地形" + more: "更多" + wiki: "維基" + live_chat: "線上聊天" + thang_main: "主要" + thang_spritesheets: "圖集" + thang_colors: "顏色" + level_some_options: "有哪些選項?" + level_tab_thangs: "物體" + level_tab_scripts: "腳本" + level_tab_settings: "設定" + level_tab_components: "組件" + level_tab_systems: "系統" + level_tab_docs: "文件" + level_tab_thangs_title: "目前物體" + level_tab_thangs_all: "所有的" + level_tab_thangs_conditions: "啟動條件" + level_tab_thangs_add: "增加物體" +# level_tab_thangs_search: "Search thangs" + add_components: "增加組件" + component_configs: "組件組態" + config_thang: "雙擊來構建物體" + delete: "刪除" + duplicate: "複製" + stop_duplicate: "停止複製" + rotate: "旋轉" + level_settings_title: "設定" + level_component_tab_title: "現在組件" + level_component_btn_new: "建立新組件" + level_systems_tab_title: "目前系統" + level_systems_btn_new: "建立新系統" + level_systems_btn_add: "增加系統" + level_components_title: "回到所有物體" + level_components_type: "類型" + level_component_edit_title: "編輯組件" + level_component_config_schema: "配置模式" + level_component_settings: "設定" + level_system_edit_title: "編輯系統" + create_system_title: "Create New System" + new_component_title: "建立新系統" + new_component_field_system: "系統" + new_article_title: "建立新文件" + new_thang_title: "建立新物體類型" + new_level_title: "建立新關卡" + new_article_title_login: "登錄以建立新文件" + new_thang_title_login: "登錄以建立新物體類型" + new_level_title_login: "登錄以建立新關卡" + new_achievement_title: "建立新成就" + new_achievement_title_login: "登錄以建立新成就" + new_poll_title: "創建一個新的投票" + new_poll_title_login: "登入去創建一個新的投票" + article_search_title: "在這搜尋文件" + thang_search_title: "在這搜尋物體類型" + level_search_title: "在這搜尋關卡" + achievement_search_title: "搜尋成就" + poll_search_title: "搜尋投票" + read_only_warning2: "注意: 您不能在這儲存任何編輯, 因為您尚未登入." + no_achievements: "尚未有任何成就加入到這關卡中." + achievement_query_misc: "關閉成就欄的雜項" + achievement_query_goals: "關閉成就欄的關卡目標" + level_completion: "關卡完成" + pop_i18n: "填寫 I18N" + tasks: "任務" + clear_storage: "清除您本機端的變更" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" -# contribute: -# page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." -# class_attributes: "Class Attributes" -# archmage_attribute_1_pref: "Knowledge in " -# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." -# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" -# join_desc_1: "Anyone can help out! Just check out our " -# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " -# join_desc_3: ", or find us in our " -# join_desc_4: "and we'll go from there!" -# join_url_email: "Email us" -# join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" -# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." -# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" -# artisan_introduction_suf: ", then this class might be for you." -# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" -# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." -# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" -# artisan_join_desc: "Use the Level Editor in these steps, give or take:" -# artisan_join_step1: "Read the documentation." -# artisan_join_step2: "Create a new level and explore existing levels." -# artisan_join_step3: "Find us in our public HipChat room for help." -# artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" -# artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" -# adventurer_forum_url: "our forum" -# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" -# adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " -# scribe_introduction_url_mozilla: "Mozilla Developer Network" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." -# contact_us_url: "Contact us" -# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" -# scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." -# diplomat_introduction_pref: "So, if there's one thing we learned from the " -# diplomat_launch_url: "launch in October" -# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." -# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" -# diplomat_i18n_page_prefix: "You can start translating our levels by going to our" -# diplomat_i18n_page: "translations page" -# diplomat_i18n_page_suffix: ", or our interface and website on GitHub." -# diplomat_join_pref_github: "Find your language locale file " -# diplomat_github_url: "on GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" -# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." -# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" -# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" -# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." -# changes_auto_save: "Changes are saved automatically when you toggle checkboxes." -# diligent_scribes: "Our Diligent Scribes:" -# powerful_archmages: "Our Powerful Archmages:" -# creative_artisans: "Our Creative Artisans:" -# brave_adventurers: "Our Brave Adventurers:" -# translating_diplomats: "Our Translating Diplomats:" -# helpful_ambassadors: "Our Helpful Ambassadors:" + article: + edit_btn_preview: "預覽" + edit_article_title: "編輯文章" -# ladder: -# please_login: "Please log in first before playing a ladder game." -# my_matches: "My Matches" -# simulate: "Simulate" -# simulation_explanation: "By simulating games you can get your game ranked faster!" -# simulate_games: "Simulate Games!" -# simulate_all: "RESET AND SIMULATE GAMES" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" -# battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " -# summary_wins: " Wins, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" -# rank_submitting: "Submitting..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" -# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." -# choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" -# tutorial_play: "Play Tutorial" -# tutorial_recommended: "Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" -# tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" -# warmup: "Warmup" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" + polls: + priority: "優先順序" + + contribute: + page_title: "貢獻" + intro_blurb: "CodeCombat 是 100% 開源! 上百位專注的玩家已經幫助我們建造到今天的程度. 加入我們並完成下一章 CodeCombat 的冒險給全世界參與!" + alert_account_message_intro: "您好!" + alert_account_message: "為了訂閱課程信件, 您將必須先登入." + archmage_introduction: "在建造遊戲中,其中一個最美好的事情是需要整合許多不同東西. 圖像, 聲音, 及時網路, 社群網路。以及許多不同層面的編程, 從底層數據庫管理, 到用戶界面的設計和實現. 這裡有許多事情需要完成, 如果您是個有經驗的程序員並且熱衷于深入處理 CodeCombat 的本質, 那麼這職業就是為您打造的. 我們將很高興擁有您的幫助來打造有史以來最優質的編程遊戲." + class_attributes: "職業說明" + archmage_attribute_1_pref: "熟悉于" + archmage_attribute_1_suf: ", 或者渴望去學習. 我們大部份的程式以這個語言寫成. 如果您是個 Ruby 或 Python 粉絲, 您將感覺到歸屬感. 它是個JavaScript, 但是擁有更佳的文法." + archmage_attribute_2: "一些程式經驗和個人的衝勁. 我們將幫助您找到方向, 但是我們不會花費太多時間訓練您." + how_to_join: "如何加入" + join_desc_1: "任何人都可加入我們! 只需確認我們的" + join_desc_2: "來開始, 並且勾選底下的條件來宣告您成為勇敢的大法師和借由郵件獲得我們最新的消息. 想要討論可做的事或者更加深入地參與? " + join_desc_3: ", 或者找到我們在" + join_desc_4: "讓我們從這開始!" + join_url_email: "發信給我們" + join_url_hipchat: "公共的HipChat房間" + archmage_subscribe_desc: "取得郵件關於新的編程機會和公告." + artisan_introduction_pref: "我們必須建造更多的關卡! 大家為了更多的內容在高聲吶喊, 但只靠我們只能建造這麼多. 現在您的工作場所就是一關; 我們的關卡編輯器是勉強可用的, 所以請小心. 只要您有新的靈感,不論從簡單的 for-loops 到" + artisan_introduction_suf: ", 那個這職業會適合您." + artisan_attribute_1: "任何的創建內容經驗都是加分的, 例如使用過Blizzard的關卡編輯器. 但不是必須的!" + artisan_attribute_2: "渴望去完成許多測試和迭代. 為了製作好關卡, 您需要把它交給別人去玩並且觀察他們如何玩, 之後準備找到一堆東西去修改." + artisan_attribute_3: "暫且擁有探險者般的忍耐力. 我們的關卡編輯器非常的陽春,有些地方非常不易使用. 我們已經提前告知囉!" + artisan_join_desc: "按照以下步驟使用關卡編輯器:" + artisan_join_step1: "閱讀文擋." + artisan_join_step2: "創建新的關卡並且探索已存在的關卡." + artisan_join_step3: "在公共的HipChat房間裡找到我們尋求幫助." + artisan_join_step4: "公佈您的關卡到論壇中尋求回饋." + artisan_subscribe_desc: "取得郵件關於關卡編輯器更新和公告." + adventurer_introduction: "讓我們清楚的定義您的角色: 您是部坦克. 您將遭受許多傷害. 我們需要玩家來嘗試我們全新的關卡並且幫助我們找出如何讓事情變得更好. 那痛苦將是巨大的; 製作優秀的遊戲是個長遠的過程並且沒有人可以第一次就把事情做對. 如果您可以忍受並且抵抗力高, 那麼這職業也許適合您." + adventurer_attribute_1: "渴望學習. 您想要學習如何編程並且我們想要教導您如何編程. 儘管您將可能在這情況中獨自完成大部分教學." + adventurer_attribute_2: "魅力十足的. 直率但如紳士般的指出需要改進的地方, 並且提供如何改進的建議." + adventurer_join_pref: "無論是與其餘工匠們一起共事, 或者勾選底下的條件來借由郵件獲得我們最新需要測試的關卡. 我們也將公佈需要評估的關卡在我們的網站上,例如:" + adventurer_forum_url: "我們的論壇" + adventurer_join_suf: "所以如果您更加喜歡借由這方式被通知, 在那些網站上登錄吧!" + adventurer_subscribe_desc: "當有新關卡需要測試時取得郵件." + scribe_introduction_pref: "CodeCombat不只是將擁有一堆關卡. 它將也包含知識資源, 一種充滿編程概念的wiki並且每道關卡都將蘊含著這樣的概念. 在這概念下, 不只每位工匠必須描述針對每個細節提出講解, 他們製作的關卡還可以簡單地和wiki中的編程概念產生連結. 某些東西已經在" + scribe_introduction_url_mozilla: "Mozilla 開發者社群" + scribe_introduction_suf: "完成. 如果您的有趣意見是可以在 Markdown 形式下闡述編程概念, 那麼這職業也許適合您." + scribe_attribute_1: "文字的技巧幾乎是您全部所需要的. 不只是文法和拼字, 但還需傳達複雜概念給閱讀的人." + contact_us_url: "聯繫我們" + scribe_join_description: "告訴我們更多關於您的資訊, 您的編程經驗和一些您喜歡寫下的東西. 我們將從這些地方開始!" + scribe_subscribe_desc: "取得公告關於寫作文章的郵件." + diplomat_introduction_pref: "所以, 如果說我們從" + diplomat_launch_url: " 十月的那次上線 " + diplomat_introduction_suf: "中得到了怎樣的啟發: 那就是在許多國家有許多人對CodeCombat產生興趣! 我們正在建立一群翻譯者,急於將一組組的英文翻譯成各國語言讓CodeCombat可以讓全世界更多人都可以接觸. 如果您喜歡搶先閱讀新內容並且讓您的國人都可以儘速的擁有, 那麼這職業也許適合您." + diplomat_attribute_1: "擁有流利的英文並且喜歡翻譯某種語言. 當傳遞複雜想法時, 您必須這兩種語言都是熟悉的!" + diplomat_i18n_page_prefix: "您可以開始從我們的" + diplomat_i18n_page: " 翻譯頁面 " + diplomat_i18n_page_suffix: "翻譯我們的關卡, 或者從我們在 GitHub 上的頁面." + diplomat_join_pref_github: "在" + diplomat_github_url: " GitHub " + diplomat_join_suf_github: "找到您的語言文件 (中文的是: codecombat/app/locale/zh-HANT.coffee), 在線編輯它, 並且上傳一個 pull 請求. 另外, 勾選底下的條件來借由郵件獲得及時的國際化開發!" + diplomat_subscribe_desc: "取得國際化開發和待翻譯關卡的郵件." + ambassador_introduction: "這是個我們正在建立的社群, 您將是我們與世界的連接點. 我們在論壇, 郵件, 社群網路上和許多人交談並且幫助彼此熟悉遊戲以及互相學習. 如果您想要幫助其它人參與並且從中獲得許多樂趣, 以及樂於感受CodeCombat的脈搏和我們將前往的地方, 那麼這職業也許適合您." + ambassador_attribute_1: "溝通技巧. 可以找到玩家正面臨的問題並且幫助他們解決. 另外, 保持與我們聯繫玩家們討論的, 喜愛的, 厭惡的以及想要的!" + ambassador_join_desc: "告訴我們更多關於您的資訊, 您已完成的事情和您喜歡做的事情. 我們將從這些地方開始!" + ambassador_join_note_strong: "注意" + ambassador_join_note_desc: "其中一件我們優先要做的事情是建立多人連線, 玩家將面臨獨自難以解決的關卡而且可以招喚更高等級的法師來幫助. 這將對於使節是一個很棒的方式來完成自己的責任. 我們會及時地向大家公佈!" + ambassador_subscribe_desc: "取得更新和多人連線開發的郵件." + changes_auto_save: "當您勾選後, 改變將自動儲存." + diligent_scribes: "我們勤奮的文書:" + powerful_archmages: "我們強勁的大法師:" + creative_artisans: "我們創意的工匠:" + brave_adventurers: "我們勇敢的冒險家:" + translating_diplomats: "我們翻譯中的外交官:" + helpful_ambassadors: "我們善於幫助的使節:" + + ladder: + please_login: "在參與對弈前請先登入." + my_matches: "我的對手" + simulate: "模擬" + simulation_explanation: "通過模擬遊戲,您可以使您的遊戲更快得到評分!" + simulate_games: "模擬遊戲!" + simulate_all: "重置並模擬遊戲" + games_simulated_by: "您模擬過的次數:" + games_simulated_for: "替您模擬的次數:" + games_simulated: "遊戲已模擬" + games_played: "玩過的遊戲" + ratio: "通過率" + leaderboard: "排行榜" + battle_as: "我要扮演 " + summary_your: "您的 " + summary_matches: "對手 - " + summary_wins: " 勝利, " + summary_losses: " 失敗" + rank_no_code: "沒有新程式碼可評分No New Code to Rank" + rank_my_game: "對我的遊戲評分!" + rank_submitting: "上傳中..." + rank_submitted: "已上傳以求評分" + rank_failed: "評分失敗" + rank_being_ranked: "已評分" + rank_last_submitted: "已上傳 " + help_simulate: "模擬遊戲需要幫助?" + code_being_simulated: "您的新代碼正在被其他人模擬評分中. 分數將隨每次新的配對而更新." + no_ranked_matches_pre: "對這個隊伍尚未有評分過的配對!" + no_ranked_matches_post: " 在別人的戰場上扮演競爭者並且回到這使您的代碼接受評分." + choose_opponent: "選擇對手" + select_your_language: "選擇您的語言!" + tutorial_play: "教學" + tutorial_recommended: "如果您尚未玩過,推薦先嘗試教學" + tutorial_skip: "略過教學" + tutorial_not_sure: "不確定發生啥事?" + tutorial_play_first: "先嘗試教學." + simple_ai: "簡單人工智慧" + warmup: "熱身" + friends_playing: "與朋友連線" + log_in_for_friends: "登入與朋友一起玩!" + social_connect_blurb: "連線並擊敗您的朋友!" + invite_friends_to_battle: "邀請您的朋友加入此戰鬥!" + fight: "戰鬥!" + watch_victory: "觀看您的勝利" + defeat_the: "擊敗" +# tournament_started: ", started" + tournament_ends: "錦標賽結束" + tournament_ended: "錦標賽已結束" + tournament_rules: "錦標賽規則" + tournament_blurb: "寫下程式碼, 收集金幣, 建立軍隊, 粉碎敵人, 贏得獎項,在我們價值$40,000的Greed錦標賽中升級您的職業! 查看" + tournament_blurb_criss_cross: "贏得競賽, 建造道路, 智勝對手, 收集寶石, 在我們的Criss-Crossand錦標賽中升級您的職業! 查看" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" + tournament_blurb_blog: "我們的部落格" + rules: "規則" + winners: "贏家" user: -# stats: "Stats" + stats: "統計" singleplayer_title: "單人遊戲等級" multiplayer_title: "多人遊戲等級" achievements_title: "成就" @@ -763,374 +1069,410 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese no_achievements: "還沒有取得成就." favorite_prefix: "語言喜好為" favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." achievements: -# last_earned: "Last Earned" + last_earned: "最近一次" amount_achieved: "數量" achievement: "成就" category_contributor: "貢獻者" -# category_ladder: "Ladder" -# category_level: "Level" -# category_miscellaneous: "Miscellaneous" + category_ladder: "升級比賽" + category_level: "等級" + category_miscellaneous: "其他" category_levels: "等級" category_undefined: "未定義" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" + current_xp_prefix: "當前總共" + current_xp_postfix: "經驗" + new_xp_prefix: "獲得" + new_xp_postfix: "經驗" + left_xp_prefix: "還需要" + left_xp_infix: "經驗" + left_xp_postfix: "到下一個等級" account: recently_played: "最近玩過" - no_recent_games: "在過去兩個星期沒有遊戲玩過。" + no_recent_games: "在過去兩個星期沒有玩過遊戲。" + payments: "付款" + purchased: "已購買" + subscription: "訂閱" +# invoices: "Invoices" + service_apple: "設備: Apple" + service_web: "設備: Web" + paid_on: "支付" + service: "服務" + price: "價格" + gems: "寶石" + active: "有效" + subscribed: "已訂閱" + unsubscribed: "取消訂閱" + active_until: "有效直到" + cost: "花費" + next_payment: "下次付款" + card: "信用卡" + status_unsubscribed_active: "您尚未訂閱並且將不會收到賬單,但是您的帳號現在仍然是有效的." + status_unsubscribed: "借由訂閱CodeCombat,取得存取新關卡,新英雄,新物品和額外寶石的資格!" -# loading_error: -# could_not_load: "Error loading from server" -# connection_failure: "Connection failed." -# unauthorized: "You need to be signed in. Do you have cookies disabled?" -# forbidden: "You do not have the permissions." -# not_found: "Not found." -# not_allowed: "Method not allowed." -# timeout: "Server timeout." -# conflict: "Resource conflict." -# bad_input: "Bad input." -# server_error: "Server error." -# unknown: "Unknown error." +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" -# social_network_apis: "Social Network APIs" -# facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" -# facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" -# gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" -# user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" -# patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" -# thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# heroes: "Heroes" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" -# feedback: "Feedback" + loading_error: + could_not_load: "從伺服器載入失敗" + connection_failure: "連線失敗." + unauthorized: "您需要先登錄. 您把cookies禁用了嗎?" + forbidden: "您沒有權限." + not_found: "沒找到." + not_allowed: "方法不被允許." + timeout: "伺服器超時." + conflict: "資源衝突." + bad_input: "錯誤輸入." + server_error: "伺服器錯誤." + unknown: "未知錯誤." -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" + resources: + sessions: "會談" + your_sessions: "您的會談" + level: "等級" + social_network_apis: "社群網路 APIs" + facebook_status: "Facebook 狀態" + facebook_friends: "Facebook 朋友" + facebook_friend_sessions: "Facebook 朋友會談" + gplus_friends: "G+ 朋友" + gplus_friend_sessions: "G+ 朋友會談" + leaderboard: "排行榜" + user_schema: "用戶模式" + user_profile: "用戶信息" + patch: "修補" + patches: "修補" + patched_model: "資源文擋" + model: "型號" + system: "系統" + systems: "系統" + component: "組建" + components: "組建" + thang: "物品" + thangs: "物品" + level_session: "您的會談" + opponent_session: "敵人會談" + article: "文章" + user_names: "用戶名稱" + thang_names: "物品名稱" + files: "檔案" + top_simulators: "頂級模擬器" + source_document: "資源文擋" + document: "文擋" + sprite_sheet: "貼圖集" + employers: "員工" + candidates: "候選人" + candidate_sessions: "候選人會談" + user_remark: "用戶備註" + user_remarks: "用戶備註" + versions: "版本" + items: "項目" +# hero: "Hero" + heroes: "英雄" + achievement: "成就" + clas: "貢獻者許可協議" + play_counts: "播放次數" + feedback: "回饋" + payment_info: "付款訊息" + campaigns: "征戰" +# poll: "Poll" +# user_polls_record: "Poll Voting History" -# guide: -# temp: "Temp" +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" + + delta: + added: "已填加" + modified: "已修改" +# not_modified: "Not Modified" + deleted: "已刪除" + moved_index: "已移動的索引" + text_diff: "文本變化" + merge_conflict_with: "合併衝突出現在" + no_changes: "沒有改變" + + guide: + temp: "臨時" multiplayer: multiplayer_title: "多人遊戲設定" # We'll be changing this around significantly soon. Until then, it's not important to translate. -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." + multiplayer_toggle: "開啟多人模式" + multiplayer_toggle_description: "允許他人加入遊戲。" multiplayer_link_description: "把這個連結告訴同伴們,一起玩吧。" multiplayer_hint_label: "提示:" multiplayer_hint: " 點擊全選,然後按 ⌘-C 或 Ctrl-C 複製連結。" multiplayer_coming_soon: "請期待更多的多人關卡!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." + multiplayer_sign_in_leaderboard: "註冊並且登入帳號,就可以將您的成就放在排行榜上." -# legal: -# page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." -# opensource_description_prefix: "Check out " -# github_url: "our GitHub" -# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " -# archmage_wiki_url: "our Archmage wiki" -# opensource_description_suffix: "for a list of the software that makes this game possible." -# practices_title: "Respectful Best Practices" -# practices_description: "These are our promises to you, the player, in slightly less legalese." -# privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." -# security_title: "Security" -# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." -# email_title: "Email" -# email_description_prefix: "We will not inundate you with spam. Through" -# email_settings_url: "your email settings" -# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." -# cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." -# copyrights_title: "Copyrights and Licenses" -# contributor_title: "Contributor License Agreement" -# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" -# cla_url: "CLA" -# contributor_description_suffix: "to which you should agree before contributing." -# code_title: "Code - MIT" -# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" -# mit_license_url: "MIT license" -# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." -# art_title: "Art/Music - Creative Commons " -# art_description_prefix: "All common content is available under the" -# cc_license_url: "Creative Commons Attribution 4.0 International License" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" -# art_music: "Music" -# art_sound: "Sound" -# art_artwork: "Artwork" -# art_sprites: "Sprites" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." -# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." -# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" -# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." -# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." -# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." -# rights_title: "Rights Reserved" -# rights_desc: "All rights are reserved for Levels themselves. This includes" -# rights_scripts: "Scripts" -# rights_unit: "Unit configuration" -# rights_description: "Description" -# rights_writings: "Writings" -# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." -# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." -# nutshell_title: "In a Nutshell" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." + legal: + page_title: "條文" + opensource_intro: "CodeCombat是完全的開源." + opensource_description_prefix: "查看 " + github_url: "我們的 GitHub" + opensource_description_center: "並且如果有興趣也歡迎您的幫助! CodeCombat是建立在許多的開源專案上, 並且我們深愛它們. 查看 " + archmage_wiki_url: "我們的大法師wiki" + opensource_description_suffix: " 尋求創造這款遊戲的相關軟體列表." + practices_title: "值得尊敬的最佳實踐" + practices_description: "這些是我們給您, 給玩家的承諾, 儘管這在法律上略顯不足." + privacy_title: "隱私" + privacy_description: "我們將不會賣出任何關於您個人的資訊." + security_title: "安全" + security_description: "我們渴望保持您的個人資訊是安全的. 身為一個開源的專案, 我們的網站是開放給任何人來檢視並且提升我們的安全系統." + email_title: "郵件" + email_description_prefix: "我們將不會使您的信箱氾濫的收到垃圾信. 不論" + email_settings_url: " 在您的郵件設定 " + email_description_suffix: "或在我們送出的信件上都有留著我們的聯結, 您可以更改您的喜好並且輕易的隨時取消訂閱." + cost_title: "花費" + cost_description: "CodeCombat在核心的關卡是全部免費, 但只要花費一個月$9.99美金來訂閱, 您將在每個月取得額外的關卡和3500顆寶石. 您可以輕易地取消訂閱並且保證取得100%的退費." + copyrights_title: "版權和許可" + contributor_title: "貢獻者許可協議" + contributor_description_prefix: "所有在本網站或是 GitHub 代碼庫上的貢獻都依照我們的" + cla_url: "貢獻者許可協議" + contributor_description_suffix: "而這在您貢獻之前就應該已经同意." + code_title: "Code - MIT" + code_description_prefix: "所有由 CodeCombat 擁有或是托管在 codecombat.com 的代码,在 GitHub 代碼庫或者 codecombat.com 數據庫,以上許可協議都依照" + mit_license_url: "MIT license" + code_description_suffix: "這包括所有 CodeCombat 公開製作關卡用的系统和組件代碼." + art_title: "藝術/音樂 - Creative Commons " + art_description_prefix: "所有共通的内容都在" + cc_license_url: "Creative Commons Attribution 4.0 International License" + art_description_suffix: "共通内容是指所有 CodeCombat 發佈出来用于製作關卡的内容。包括:" + art_music: "音樂" + art_sound: "聲音" + art_artwork: "藝術作品" + art_sprites: "貼圖" + art_other: "所有製作關卡時公開的,不是代碼的創造性產品." + art_access: "目前還没有簡便通用的下载素材方式. 一般来說,從網站上使用的URL下载,或者聯繫我们尋求幫助. 當然您也可以幫助我们拓展網站,使這些資源更容易下載." + art_paragraph_1: "為了歸屬, 請在使用處或適當的地方說明, 或者留下一個聯結至codecombat.com. 例如:" + use_list_1: "如果使用在電影或者另一款遊戲中, 請在製作人員表中加入codecombat.com." + use_list_2: "如果使用在網站上, 請在使用處留下codecombat.com, 例如在圖片下面或一個您專門放置Creative Commons和開源專案的地方. 如果您的内容明確提到關於 CodeCombat, 那您就不需要額外署名." + art_paragraph_2: "如果您使用的內容非由CodeCombat製作但由一位codecombat.com中的使用者, 那您應該署名他. 並且如果相對應的頁面上有標記署名指示, 那您應該遵照其指示." + rights_title: "保留權利" + rights_desc: "所有版權由關卡本身擁有, 這包含:" + rights_scripts: "腳本" + rights_unit: "單元配置" + rights_description: "描述" + rights_writings: "寫作" + rights_media: "聲音、音樂以及其他專門為某道關卡製作, 而不對其他關卡開放的創造性内容." + rights_clarification: "澄清一下, 所有在關卡编辑器裡公開用於制作關卡的資源都是在CC協議下發佈的, 然而使用關卡编辑器製作,或者在關卡製作過程中上傳的内容则不是." + nutshell_title: "簡而言之" + nutshell_description: "我们在關卡编辑器里公開的任何資源, 您都可以在製作關卡時隨意使用, 但我们保留在 codecombat.com 之上創建的關卡本身傳播的權利, 因为我们往後可能決定以它們收費." + canonical: "我們宣告這篇說明的英文版本是權威版本. 如果各個翻譯版本之間有任何衝突, 以英文版為準." -# ladder_prizes: -# title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" + ladder_prizes: + title: "錦標賽獎項" # This section was for an old tournament and doesn't need new translations now. + blurb_1: "依據" + blurb_2: "錦標賽規則" + blurb_3: ", 這些獎項將被頒發給頂尖的人類和怪物玩家." + blurb_4: "兩隊意指獎項是兩倍!" + blurb_5: "(將有兩個第一名, 兩個第二名, etc.)" + rank: "排名" + prizes: "獎項" + total_value: "總價值" + in_cash: "現金" + custom_wizard: "客制 CodeCombat 巫師" + custom_avatar: "客制 CodeCombat 頭像" + heap: "給六個月的\"Startup\"訪問" + credits: "信譽" + one_month_coupon: "固本: 選擇 Rails 或者 HTML" + one_month_discount: "折扣 30% : 選擇 Rails 或者 HTML" + license: "許可證" + oreilly: "你選擇的電子書" account_profile: -# settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" + settings: "設定" # We are not actively recruiting right now, so there's no need to add new translations for this section. + edit_profile: "編輯基本資料" + done_editing: "完成編輯" profile_for_prefix: "關於" profile_for_suffix: "的基本資料" -# featured: "Featured" -# not_featured: "Not Featured" -# looking_for: "Looking for:" -# last_updated: "Last updated:" -# contact: "Contact" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." -# next_name: "name?" -# next_short_description: "write a short description." -# next_long_description: "describe your desired position." -# next_skills: "list at least five skills." -# next_work: "chronicle your work history." -# next_education: "recount your educational ordeals." -# next_projects: "show off up to three projects you've worked on." -# next_links: "add any personal or social links." -# next_photo: "add an optional professional photo." -# next_active: "mark yourself open to offers to show up in searches." -# example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" -# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" -# links_name_help: "What are you linking to?" -# links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" -# basics_city: "City" -# basics_city_help: "City you want to work in (or live in now)." -# basics_country: "Country" -# basics_country_help: "Country you want to work in (or live in now)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" -# basics_looking_for_full_time: "Full-time" -# basics_looking_for_part_time: "Part-time" -# basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." -# work_experience: "Work Experience" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" -# work_duration_help: "When did you hold this gig?" -# work_description: "Description" -# work_description_help: "What did you do there? (140 chars; optional)" -# education: "Education" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." -# education_school: "School" -# education_school_help: "Name of your school." -# education_degree: "Degree" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" -# our_notes: "CodeCombat's Notes" -# remarks: "Remarks" -# projects: "Projects" -# projects_header: "Add 3 projects" -# projects_header_2: "Projects (Top 3)" -# projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" -# project_name_help: "What was the project called?" -# project_description: "Description" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" -# project_link_help: "Link to the project." -# player_code: "Player Code" + featured: "被推薦" + not_featured: "未獲選" + looking_for: "尋找:" + last_updated: "最後更新:" + contact: "聯繫" + active: "目前尋找面試機會" + inactive: "目前沒有在尋找面試機會" + complete: "完成" + next: "下一步" + next_city: "城市?" + next_country: "選擇您所居住的國家." + next_name: "姓名?" + next_short_description: "簡短的敘述您自己." + next_long_description: "描述您所需要的位置." + next_skills: "列出至少五項技能." + next_work: "您的過往經歷." + next_education: "教育經歷." + next_projects: "至少展示三項您所參與的專案." + next_links: "增加任何個人或社群的聯結." + next_photo: "增加一張專業的照片(可選)." + next_active: "將自己標記正在尋求工作機會以使自己的名字出現在搜索中." + example_blog: "部落格" + example_personal_site: "個人網站" + links_header: "個人聯結" + links_blurb: "聯結任何您想凸顯的網站或介紹, 例如:聯結您的GitHub, 您的LinkedIn, 您的部落格." + links_name: "聯結名稱" + links_name_help: "您想聯結到?" + links_link_blurb: "聯結網址" + basics_header: "更新基本的資訊" + basics_active: "接受工作邀請" + basics_active_help: "目前想要獲得面試機會?" + basics_job_title: "期望職位" + basics_job_title_help: "您正在尋找怎樣的職位?" + basics_city: "城市" + basics_city_help: "您想工作的城市(或者現在居住地)." + basics_country: "國家" + basics_country_help: "您想工作的國家(或者現在居住地)." + basics_visa: "美國工作簽證狀態" + basics_visa_help: "您是否在美國可以合法工作, 或者您需要公司資助簽證? (如果您住在Canada或Australia, 標注可以合法工作.)" + basics_looking_for: "尋找" + basics_looking_for_full_time: "全職" + basics_looking_for_part_time: "兼職" + basics_looking_for_remote: "遠端工作" + basics_looking_for_contracting: "約娉工作" + basics_looking_for_internship: "實習" + basics_looking_for_help: "您想要哪種的開發者職位?" + name_header: "填寫您的姓名" + name_anonymous: "匿名開發者" + name_help: "您希望僱主看到的姓名, 例如: 'Nick Winter'." + short_description_header: "簡短的敘述您自己" + short_description_blurb: "增加一則簡短介紹來幫助僱主快速的認識您." + short_description: "簡短介紹" + short_description_help: "您是誰, 您正在尋找啥? 最多 140 字." + skills_header: "技能" + skills_help: "按照熟練程度列出您所擁有的相關開發技能." + long_description_header: "描述您所希望的位置" + long_description_blurb: "告訴僱主您有多棒和您所希望扮演的角色." + long_description: "自我描述" + long_description_help: "向潛在僱主描述您自己. 保持簡短並且直指核心. 我們建議您列出最有興趣的職位; 最多600字." + work_experience: "工作經歷" + work_header: "按時間順序列出經歷" + work_years: "工作時間(年)" + work_years_help: "您擁有多少年的專業開發軟體的經驗(被支薪)?" + work_blurb: "列出您的相關工作經驗, 最近的優先." + work_employer: "僱主" + work_employer_help: "您僱主的姓名." + work_role: "職稱" + work_role_help: "您的職稱或者角色是?" + work_duration: "起止時間" + work_duration_help: "您的在職時間是?" + work_description: "描述" + work_description_help: "您在那是在做啥? (140 字; 可選)" + education: "教育" + education_header: "列出您的教育經歷" + education_blurb: "列出您在學校的經歷." + education_school: "學校" + education_school_help: "學校名稱." + education_degree: "學位" + education_degree_help: "您的學位以及專業是?" + education_duration: "日期" + education_duration_help: "何時?" + education_description: "描述" + education_description_help: "凸顯任何有關這個教育經歷的地方. (140 字; 可選)" + our_notes: "CodeCombat的筆記" + remarks: "標記" + projects: "專案" + projects_header: "增加3個專案" + projects_header_2: "專案(前3個)" + projects_blurb: "展示您的專案以求驚豔僱主." + project_name: "專案名稱" + project_name_help: "那個專案被稱為什麼?" + project_description: "描述" + project_description_help: "簡短描述專案." + project_picture: "照片" + project_picture_help: "上傳一張230x115px或更大的照片以顯示那份專案." + project_link: "聯結" + project_link_help: "專案的聯結." + player_code: "玩家代碼" -# employers: -# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now." -# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being." -# hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. -# get_started: "Get Started" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." -# candidate_name: "Name" -# candidate_location: "Location" -# candidate_looking_for: "Looking For" -# candidate_role: "Role" -# candidate_top_skills: "Top Skills" -# candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" + employers: + deprecation_warning_title: "抱歉, CodeCombat現在並沒有在徵才." + deprecation_warning: "我們暫且主要專注在找到初級有潛力的開發者而非高級的開發者." + hire_developers_not_credentials: "僱用真正的開發者, 而非一張證書." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section. + get_started: "開始" + already_screened: "我們已經技術上考核過我們所有的候選人" + filter_further: ", 但是您還是可以更加嚴苛的過濾:" + filter_visa: "簽證" + filter_visa_yes: "美國授權" + filter_visa_no: "不被授權" + filter_education_top: "頂尖學校" + filter_education_other: "其他" + filter_role_web_developer: "網頁開發者" + filter_role_software_developer: "軟體開發者" + filter_role_mobile_developer: "移動開發者" + filter_experience: "經驗" + filter_experience_senior: "初級的" + filter_experience_junior: "高級的" + filter_experience_recent_grad: "剛畢業" + filter_experience_student: "大學學生" + filter_results: "結果" + start_hiring: "開始徵才." + reasons: "三個您應該透過我們徵才的理由:" + everyone_looking: "在這的每個人都在尋找各自的下一個機會." + everyone_looking_blurb: "不要再理會LinkedIn的20%郵件回復率. 我們列在這網站上的每個人都想要找到各自的下一個位置並且都將回復您以求得機會." + weeding: "仔細聽好; 我們已經幫您做了過濾." + weeding_blurb: "每位我們列出來的玩家已經考核過技術能力. 我們也對候選人執行電話訪談並且在他們的基本資料上留下記錄,以求節省您的時間." + pass_screen: "他們將通過您的技術能力考核." + pass_screen_blurb: "在聯絡前先檢視每位候選人的程式碼. 一位僱主發現我們列出的候選人能通過考核的人數是借由Hacker News的5倍." + make_hiring_easier: "請促使我們的僱用流程簡單點." + what: "CodeCombat是啥?" + what_blurb: "CodeCombat是個跑在瀏覽器上的多人編程遊戲. 玩家通過編寫程式碼來控制在戰場上的角色來擊潰其它玩家的角色. 我們的玩家有著全部主要的技術經驗." + cost: "我們收取多少費用?" + cost_blurb: "我們將收取員工第一年薪水的15%當作佣金. 佣金須在僱用時就必須付清並且之後的90天內如果員工離職則可退款. 我們不對已經參與貴公司面試的候選人收費." + candidate_name: "姓名" + candidate_location: "地點" + candidate_looking_for: "尋找" + candidate_role: "角色" + candidate_top_skills: "高級技能" + candidate_years_experience: "多年工作經驗" + candidate_last_updated: "最後一次更新" + candidate_who: "誰" + featured_developers: "主要開發者" + other_developers: "其他開發者" + inactive_developers: "不活躍的開發者" -# admin: -# av_espionage: "Espionage" # Really not important to translate /admin controls. -# av_espionage_placeholder: "Email or username" -# av_usersearch: "User Search" -# av_usersearch_placeholder: "Email, username, name, whatever" -# av_usersearch_search: "Search" -# av_title: "Admin Views" -# av_entities_sub_title: "Entities" -# av_entities_users_url: "Users" -# av_entities_active_instances_url: "Active Instances" -# av_entities_employer_list_url: "Employer List" -# av_entities_candidates_list_url: "Candidate List" -# av_entities_user_code_problems_list_url: "User Code Problems List" -# av_other_sub_title: "Other" -# av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "User List" -# ucp_title: "User Code Problems" -# lg_title: "Latest Games" -# clas: "CLAs" + admin: + av_espionage: "Espionage" # Really not important to translate /admin controls. + av_espionage_placeholder: "信箱或用戶名" + av_usersearch: "用戶搜尋" + av_usersearch_placeholder: "信箱, 用戶名, 姓名, 任何東西" + av_usersearch_search: "搜尋" + av_title: "管理員視野" + av_entities_sub_title: "商業個體" + av_entities_users_url: "使用者" + av_entities_active_instances_url: "有效實例" + av_entities_employer_list_url: "員工列表" + av_entities_candidates_list_url: "候選人列表" + av_entities_user_code_problems_list_url: "用戶代碼問題列表" + av_other_sub_title: "其他" + av_other_debug_base_url: "Base (用於測試 base.jade)" + u_title: "用戶列表" + ucp_title: "用戶代碼問題" + lg_title: "最新遊戲" + clas: "貢獻者許可協議" diff --git a/app/locale/zh-WUU-HANS.coffee b/app/locale/zh-WUU-HANS.coffee index df7a82580..0527bc041 100644 --- a/app/locale/zh-WUU-HANS.coffee +++ b/app/locale/zh-WUU-HANS.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # slogan: "Learn to Code by Playing a Game" # no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older # no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices -# play: "Play" # The big play button that just starts playing a level +# play: "Play" # The big play button that opens up the campaign view. # old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari # old_browser_suffix: "You can try anyway, but it probably won't work." +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." # campaign: "Campaign" # for_beginners: "For Beginners" # multiplayer: "Multiplayer" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" # level_difficulty: "Difficulty: " # campaign_beginner: "Beginner Campaign" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." -# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important. -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_old_beginner: "Old Beginner Campaign" -# campaign_old_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." +# adjust_volume: "Adjust volume" # campaign_multiplayer: "Multiplayer Arenas" # campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" # login: # sign_up: "Create Account" # log_in: "Log In" # logging_in: "Logging In" # log_out: "Log Out" -# recover: "recover account" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" # signup: -# create_account_title: "Create Account to Save Progress" -# description: "It's free. Just need a couple things and you'll be good to go:" # email_announcements: "Receive announcements by email" -# coppa: "13+ or non-USA " -# coppa_why: "(Why?)" # creating: "Creating Account..." # sign_up: "Sign Up" # log_in: "log in with password" # social_signup: "Or, you can sign up through Facebook or G+:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" # recover: # recover_account_title: "Recover Account" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "徕搭读取……" # saving: "Saving..." # sending: "Sending..." @@ -139,9 +140,14 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # fork: "Fork" # play: "Play" # When used as an action verb, like "Play next level" # retry: "Retry" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" # general: # and: "and" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # date: "Date" # body: "Body" # version: "Version" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" # commit_msg: "Commit Message" +# review: "Review" # version_history: "Version History" # version_history_for: "Version History for: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" # result: "Result" # results: "Results" # description: "Description" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # hard: "Hard" # player: "Player" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" # units: # second: "second" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # reload_title: "Reload All Code?" # reload_really: "Are you sure you want to reload this level back to the beginning?" # reload_confirm: "Reload All" +# victory: "Victory" # victory_title_prefix: "" # victory_title_suffix: " Complete" # victory_sign_up: "Sign Up to Save Progress" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # victory_rate_the_level: "Rate the level: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" -# victory_play_next_level: "Play Next Level" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" # victory_go_home: "Go Home" # Only in old-style levels. # victory_review: "Tell us more!" # Only in old-style levels. # victory_hour_of_code_done: "Are You Done?" # victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" # guide_title: "Guide" # tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. # tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). # tome_select_a_thang: "Select Someone for " # tome_available_spells: "Available Spells" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # loading_ready: "Ready!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" # infinite_loop_try_again: "Try Again" # infinite_loop_reset_level: "Reset Level" # infinite_loop_comment_out: "Comment Out My Code" # tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." +# tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward." # tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info." # tip_open_source: "CodeCombat is 100% open source!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" # tip_beta_launch: "CodeCombat launched its beta in October, 2013." # tip_think_solution: "Think of the solution, not the problem." # tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" -# customize_wizard: "Customize Wizard" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." # game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" # multiplayer_tab: "Multiplayer" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." # editor_config: "Editor Config" # editor_config_title: "Editor Configuration" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." # versions: # save_version_title: "Save New Version" # new_major_version: "New Major Version" +# submitting_patch: "Submitting Patch..." # cla_prefix: "To save changes, first you must agree to our" # cla_url: "CLA" # cla_suffix: "." # cla_agree: "I AGREE" +# owner_approve: "An owner will need to approve it before your changes will become visible." # contact: # contact_us: "Contact CodeCombat" # welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" # forum_prefix: "For anything public, please try " # forum_page: "our forum" # forum_suffix: " instead." +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" # send: "Send Feedback" # contact_candidate: "Contact Candidate" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # autosave: "Changes Save Automatically" # me_tab: "Me" # picture_tab: "Picture" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" # password_tab: "Password" # emails_tab: "Emails" # admin: "Admin" # new_password: "New Password" # new_password_verify: "Verify" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." # email_subscriptions: "Email Subscriptions" # email_subscriptions_none: "No Email Subscriptions." # email_announcements: "Announcements" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." # sample_profile: "See a sample profile" # view_profile: "View Your Profile" -# wizard_tab: "Wizard" -# wizard_color: "Wizard Clothes Color" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + # classes: # archmage_title: "Archmage" # archmage_title_description: "(Coder)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" # artisan_title: "Artisan" # artisan_title_description: "(Level Builder)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." # adventurer_title: "Adventurer" # adventurer_title_description: "(Level Playtester)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." # scribe_title: "Scribe" # scribe_title_description: "(Article Editor)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." # diplomat_title: "Diplomat" # diplomat_title_description: "(Translator)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." # ambassador_title: "Ambassador" # ambassador_title_description: "(Support)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." # editor: # main_title: "CodeCombat Editors" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # thang_title: "Thang Editor" # level_title: "Level Editor" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" # back: "Back" # revert: "Revert" # revert_models: "Revert Models" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" # fork_title: "Fork New Version" # fork_creating: "Creating Fork..." # generate_terrain: "Generate Terrain" # more: "More" # wiki: "Wiki" # live_chat: "Live Chat" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" # level_tab_scripts: "Scripts" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # level_tab_thangs_all: "All" # level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_add: "Add Thangs" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" # delete: "Delete" # duplicate: "Duplicate" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" # level_settings_title: "Settings" # level_component_tab_title: "Current Components" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" # article_search_title: "Search Articles Here" # thang_search_title: "Search Thang Types Here" # level_search_title: "Search Levels Here" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" # read_only_warning2: "Note: you can't save any edits here, because you're not logged in." # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" # article: # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" +# polls: +# priority: "Priority" + # contribute: # page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" # alert_account_message_intro: "Hey there!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -631,10 +948,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" # scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" # diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." @@ -677,15 +984,12 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # diplomat_join_pref_github: "Find your language locale file " # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" # diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." +# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." # changes_auto_save: "Changes are saved automatically when you toggle checkboxes." # diligent_scribes: "Our Diligent Scribes:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" # loading_error: # could_not_load: "Error loading from server" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # leaderboard: "Leaderboard" # user_schema: "User Schema" # user_profile: "User Profile" +# patch: "Patch" # patches: "Patches" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -869,7 +1233,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # legal: # page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." +# opensource_intro: "CodeCombat is completely open source." # opensource_description_prefix: "Check out " # github_url: "our GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # practices_title: "Respectful Best Practices" # practices_description: "These are our promises to you, the player, in slightly less legalese." # privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." +# privacy_description: "We will not sell any of your personal information." # security_title: "Security" # security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." # email_title: "Email" @@ -886,13 +1250,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # email_settings_url: "your email settings" # email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." # cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." +# cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee." # copyrights_title: "Copyrights and Licenses" # contributor_title: "Contributor License Agreement" # contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" @@ -926,7 +1284,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." # nutshell_title: "In a Nutshell" # nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." +# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepancies between translations, the English document takes precedence." # ladder_prizes: # title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now. @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # license: "license" # oreilly: "ebook of your choice" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - # account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/locale/zh-WUU-HANT.coffee b/app/locale/zh-WUU-HANT.coffee index 5cd0c9b1e..db8947e9f 100644 --- a/app/locale/zh-WUU-HANT.coffee +++ b/app/locale/zh-WUU-HANT.coffee @@ -3,9 +3,10 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio slogan: "打遊戲來學編程" no_ie: "對弗住!箇網站叻 Internet Explorer 8 箇粒老個瀏覽器嘸處用。" # Warning that only shows up in IE8 and older no_mobile: "CodeCombat 勿是照手機設備設計個,怪得嘸數达弗到頂讚個享受!" # Warning that shows up on mobile devices - play: "遊戲開打" # The big play button that just starts playing a level + play: "遊戲開打" # The big play button that opens up the campaign view. old_browser: "啊耶, 爾個瀏覽器忒老哉, 嘸處運行 CodeCombat。對弗住險!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser_suffix: "爾試叻好試多遍,不過嘸大用場個。" +# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval." campaign: "打仗模式" for_beginners: "適合學起頭個人" multiplayer: "聚隊打遊戲" # Not currently shown on home page @@ -56,62 +57,60 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # confirm: "Confirm" # owned: "Owned" # For items you own # locked: "Locked" +# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased # available: "Available" # skills_granted: "Skills Granted" # Property documentation details # heroes: "Heroes" # Tooltip on hero shop button from /play # achievements: "Achievements" # Tooltip on achievement list button from /play # account: "Account" # Tooltip on account button from /play # settings: "Settings" # Tooltip on settings button from /play +# poll: "Poll" # Tooltip on poll button from /play # next: "Next" # Go from choose hero to choose inventory before playing a level # change_hero: "Change Hero" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" # buy_gems: "Buy Gems" -# older_campaigns: "Older Campaigns" +# subscription_required: "Subscription Required" # anonymous: "Anonymous Player" level_difficulty: "難度:" campaign_beginner: "新手打仗" -# awaiting_levels_adventurer_prefix: "We release five levels per week." +# awaiting_levels_adventurer_prefix: "We release new levels every week." # awaiting_levels_adventurer: "Sign up as an Adventurer" # awaiting_levels_adventurer_suffix: "to be the first to play new levels." - choose_your_level: "揀關數" # The rest of this section is the old play view at /play-old and isn't very important. - adventurer_prefix: "下底個關數候爾揀,要勿聊聊上頭箇許關數。到" - adventurer_forum: "冒險者論壇" - adventurer_suffix: "。" -# campaign_old_beginner: "Old Beginner Campaign" - campaign_old_beginner_description: "……徠箇裏爾學得到編程手法。" - campaign_dev: "照摸難關" - campaign_dev_description: "……徠箇搭爾學得到做一許囉唆功能個接口。" +# adjust_volume: "Adjust volume" campaign_multiplayer: "多人競賽場" campaign_multiplayer_description: "……徠箇搭爾好搭別人代碼捉跤。" - campaign_player_created: "造玩家" - campaign_player_created_description: "……徠箇搭爾好搭爾夥計造起來個賭打 <a href=\"/contribute#artisan\">技術相幫</a>." -# campaign_classic_algorithms: "Classic Algorithms" -# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" +# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas" +# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." + +# share_progress_modal: +# blurb: "You’re making great progress! Tell your parent how much you've learned with CodeCombat." +# email_invalid: "Email address invalid." +# form_blurb: "Enter your parent's email below and we’ll show them!" +# form_label: "Email Address" +# placeholder: "email address" +# title: "Excellent Work, Apprentice" login: sign_up: "註冊" log_in: "登進去" logging_in: "徠搭登進" log_out: "登出" - recover: "賬號尋轉" +# forgot_password: "Forgot your password?" # authenticate_gplus: "Authenticate G+" # load_profile: "Load G+ Profile" -# load_email: "Load G+ Email" # finishing: "Finishing" +# sign_in_with_facebook: "Sign in with Facebook" +# sign_in_with_gplus: "Sign in with G+" +# signup_switch: "Want to create an account?" signup: - create_account_title: "做新賬號來存進度" - description: "免費省力咦快進:" email_announcements: "用電子郵箱收通知" - coppa: "13歲以上要勿美國外" - coppa_why: " 爲何?" creating: "徠搭做賬號……" sign_up: "註冊" log_in: "登進" social_signup: "要勿,爾好用Facebook搭G+註冊:" # required: "You need to log in before you can go that way." +# login_switch: "Already have an account?" recover: recover_account_title: "賬號尋轉" @@ -127,6 +126,8 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # books: "Books" common: +# back: "Back" # When used as an action verb, like "Navigate backward" +# continue: "Continue" # When used as an action verb, like "Continue forward" loading: "徠搭讀取……" saving: "徠搭存檔……" sending: "徠搭發送……" @@ -139,9 +140,14 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio fork: "派生" play: "開來" # When used as an action verb, like "Play next level" retry: "轉試" +# actions: "Actions" +# info: "Info" +# help: "Help" # watch: "Watch" # unwatch: "Unwatch" # submit_patch: "Submit Patch" +# submit_changes: "Submit Changes" +# save_changes: "Save Changes" general: and: "搭" @@ -149,9 +155,22 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # date: "Date" body: "正文" version: "版本" +# pending: "Pending" +# accepted: "Accepted" +# rejected: "Rejected" +# withdrawn: "Withdrawn" +# submitter: "Submitter" +# submitted: "Submitted" commit_msg: "提交訊息" +# review: "Review" version_history: "版本歷史" version_history_for: "版本歷史: " +# select_changes: "Select two changes below to see the difference." +# undo_prefix: "Undo" +# undo_shortcut: "(Ctrl+Z)" +# redo_prefix: "Redo" +# redo_shortcut: "(Ctrl+Shift+Z)" +# play_preview: "Play preview of current level" result: "結果" results: "結果" description: "描述" @@ -174,6 +193,9 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio hard: "煩難" player: "來個人" # player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard +# warrior: "Warrior" +# ranger: "Ranger" +# wizard: "Wizard" units: second: "秒" @@ -214,6 +236,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio reload_title: "轉讀取全部個代碼?" reload_really: "準定轉讀取箇關,回轉到扣起頭?" reload_confirm: "轉讀取全部" +# victory: "Victory" victory_title_prefix: "" victory_title_suffix: "妝下落" victory_sign_up: "存檔進度" @@ -221,17 +244,16 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio victory_rate_the_level: "箇關評價:" # Only in old-style levels. victory_return_to_ladder: "走轉" # victory_play_continue: "Continue" -# victory_play_skip: "Skip Ahead" - victory_play_next_level: "下關" -# victory_play_more_practice: "More Practice" -# victory_play_too_easy: "Too Easy" -# victory_play_just_right: "Just Right" -# victory_play_too_hard: "Too Hard" # victory_saving_progress: "Saving Progress" victory_go_home: "轉到主頁" # Only in old-style levels. victory_review: "搭我裏反應!" # Only in old-style levels. victory_hour_of_code_done: "爾妝下落爻噃?" victory_hour_of_code_done_yes: "正是, 妝下落爻!" +# victory_experience_gained: "XP Gained" +# victory_gems_gained: "Gems Gained" +# victory_new_item: "New Item" +# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks." +# victory_become_a_viking: "Become a Viking" guide_title: "指南" tome_minion_spells: "下手個咒語" # Only in old-style levels. tome_read_only_spells: "只讀個咒語" # Only in old-style levels. @@ -242,10 +264,11 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # tome_submit_button: "Submit" # tome_reload_method: "Reload original code for this method" # Title text for individual method reload button. # tome_select_method: "Select a Method" -# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos). +# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methods). tome_select_a_thang: "揀人來 " tome_available_spells: "好用個法術" # tome_your_skills: "Your Skills" +# tome_help: "Help" # tome_current_method: "Current Method" # hud_continue_short: "Continue" # code_saved: "Code Saved" @@ -254,16 +277,23 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio loading_ready: "讀取下落!" # loading_start: "Start Level" # problem_alert_title: "Fix Your Code" +# problem_alert_help: "Help" time_current: "瑲朞:" time_total: "頂大:" time_goto: "轉到:" +# non_user_code_problem_title: "Unable to Load Level" +# infinite_loop_title: "Infinite Loop Detected" +# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know." +# check_dev_console: "You can also open the developer console to see what might be going wrong." +# check_dev_console_link: "(instructions)" infinite_loop_try_again: "轉試試試相" infinite_loop_reset_level: "轉定等級" infinite_loop_comment_out: "爲我個代碼加註解" tip_toggle_play: "用 Ctrl+P 暫停/繼續" - tip_scrub_shortcut: "用 Ctrl+[ 搭 Ctrl+] 倒退搭快進。" + tip_scrub_shortcut: "用 Ctrl+[ 搭 Ctrl+] 倒退搭快進。" # {change} tip_guide_exists: "點頁面上向個指南,望無數有用個訊息。" tip_open_source: "CodeCombat 是 百分百 開源個!" +# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_beta_launch: "CodeCombat 從 2013年10月起。" tip_think_solution: "思考解決方法,勿是問題。" tip_theory_practice: "來理論研究裏向,理論搭實踐弗分個。不過徠實踐裏頭,渠裏是有分個。 - Yogi Berra" @@ -289,13 +319,33 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "自設定獻路人" +# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..." +# tip_superpower: "Coding is the closest thing we have to a superpower." +# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds" +# tip_no_code: "No code is faster than no code." +# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries" +# tip_reusable_software: "Before software can be reusable it first has to be usable." +# tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’" +# tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates" +# tip_source_code: "I want to change the world but they would not give me the source code." +# tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann" +# tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr." +# tip_google: "Have a problem you can't solve? Google it!" +# tip_adding_evil: "Adding a pinch of evil." +# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven" +# tip_open_source_contribute: "You can help CodeCombat improve!" +# tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch" +# tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus" +# tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha" +# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." game_menu: # inventory_tab: "Inventory" # save_load_tab: "Save/Load" # options_tab: "Options" # guide_tab: "Guide" +# guide_video_tutorial: "Video Tutorial" +# guide_tips: "Tips" multiplayer_tab: "多人遊戲" # auth_tab: "Sign Up" # inventory_caption: "Equip your hero" @@ -306,9 +356,24 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # multiplayer_caption: "Play with friends!" # auth_caption: "Save your progress." +# leaderboard: +# leaderboard: "Leaderboard" +# view_other_solutions: "View Leaderboards" +# scores: "Scores" +# top_players: "Top Players by" +# day: "Today" +# week: "This Week" +# all: "All-Time" +# time: "Time" +# damage_taken: "Damage Taken" +# damage_dealt: "Damage Dealt" +# difficulty: "Difficulty" +# gold_collected: "Gold Collected" + # inventory: # choose_inventory: "Equip Items" # equipped_item: "Equipped" +# required_purchase_title: "Required" # available_item: "Available" # restricted_title: "Restricted" # should_equip: "(double-click to equip)" @@ -325,6 +390,80 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # purchasing: "Purchasing..." # declined: "Your card was declined" # retrying: "Server error, retrying." +# prompt_title: "Not Enough Gems" +# prompt_body: "Do you want to get more?" +# prompt_button: "Enter Shop" +# recovered: "Previous gems purchase recovered. Please refresh the page." +# price: "x3500 / mo" + +# subscribe: +# comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" +# feature1: "100+ basic levels across 4 worlds" +# feature2: "10 powerful <strong>new heroes</strong> with unique skills!" +# feature3: "70+ bonus levels" +# feature4: "<strong>3500 bonus gems</strong> every month!" +# feature5: "Video tutorials" +# feature6: "Premium email support" +# feature7: "Private <strong>Clans</strong>" +# free: "Free" +# month: "month" +# subscribe_title: "Subscribe" +# unsubscribe: "Unsubscribe" +# confirm_unsubscribe: "Confirm Unsubscribe" +# never_mind: "Never Mind, I Still Love You" +# thank_you_months_prefix: "Thank you for supporting us these last" +# thank_you_months_suffix: "months." +# thank_you: "Thank you for supporting CodeCombat." +# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better." +# unsubscribe_feedback_placeholder: "O, what have we done?" +# parent_button: "Ask your parent" +# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." +# parent_email_input_invalid: "Email address invalid." +# parent_email_input_label: "Parent email address" +# parent_email_input_placeholder: "Enter parent email" +# parent_email_send: "Send Email" +# parent_email_sent: "Email sent!" +# parent_email_title: "What's your parent's email?" +# parents: "For Parents" +# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?" +# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing." +# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?" +# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers." +# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." +# payment_methods: "Payment Methods" +# payment_methods_title: "Accepted Payment Methods" +# payment_methods_blurb1: "We currently accept credit cards and Alipay." +# payment_methods_blurb2: "If you require an alternate form of payment, please contact" +# stripe_description: "Monthly Subscription" +# subscription_required_to_play: "You'll need a subscription to play this level." +# unlock_help_videos: "Subscribe to unlock all video tutorials." +# personal_sub: "Personal Subscription" # Accounts Subscription View below +# loading_info: "Loading subscription information..." +# managed_by: "Managed by" +# will_be_cancelled: "Will be cancelled on" +# currently_free: "You currently have a free subscription" +# currently_free_until: "You currently have a free subscription until" +# was_free_until: "You had a free subscription until" +# managed_subs: "Managed Subscriptions" +# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" +# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide." +# group_discounts: "Group discounts" +# group_discounts_1: "We also offer group discounts for bulk subscriptions." +# group_discounts_1st: "1st subscription" +# group_discounts_full: "Full price" +# group_discounts_2nd: "Subscriptions 2-11" +# group_discounts_20: "20% off" +# group_discounts_12th: "Subscriptions 12+" +# group_discounts_40: "40% off" +# subscribing: "Subscribing..." +# recipient_emails_placeholder: "Enter email address to subscribe, one per line." +# subscribe_users: "Subscribe Users" +# users_subscribed: "Users subscribed:" +# no_users_subscribed: "No users subscribed, please double check your email addresses." +# current_recipients: "Current Recipients" +# unsubscribing: "Unsubscribing..." +# subscribe_prepaid: "Click Subscribe to use prepaid code" +# using_prepaid: "Using prepaid code for monthly subscription" # choose_hero: # choose_hero: "Choose Your Hero" @@ -339,6 +478,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # lua_blurb: "Game scripting language." # io_blurb: "Simple but obscure." # status: "Status" +# hero_type: "Type" # weapons: "Weapons" # weapons_warrior: "Swords - Short Range, No Magic" # weapons_ranger: "Crossbows, Guns - Long Range, No Magic" @@ -349,7 +489,19 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # regeneration: "Regeneration" # range: "Range" # As in "attack or visual range" # blocks: "Blocks" # As in "this shield blocks this much damage" +# backstab: "Backstab" # As in "this dagger does this much backstab damage" # skills: "Skills" +# attack_1: "Deals" +# attack_2: "of listed" +# attack_3: "weapon damage." +# health_1: "Gains" +# health_2: "of listed" +# health_3: "armor health." +# speed_1: "Moves at" +# speed_2: "meters per second." +# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store +# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) +# restricted_to_certain_heroes: "Only certain heroes can play this level." # skill_docs: # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this @@ -378,8 +530,6 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." editor_config: "編寫器設定" editor_config_title: "編寫器設定" # editor_config_level_language_label: "Language for This Level" @@ -412,34 +562,128 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # press_paragraph_1_link: "press packet" # press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." # team: "Team" -# george_title: "CEO" +# george_title: "Cofounder" # george_blurb: "Businesser" -# scott_title: "Programmer" +# scott_title: "Cofounder" # scott_blurb: "Reasonable One" -# nick_title: "Programmer" +# nick_title: "Cofounder" # nick_blurb: "Motivation Guru" # michael_title: "Programmer" # michael_blurb: "Sys Admin" -# matt_title: "Programmer" +# matt_title: "Cofounder" # matt_blurb: "Bicyclist" +# cat_title: "Chief Artisan" +# cat_blurb: "Airbender" +# josh_title: "Game Designer" +# josh_blurb: "Floor Is Lava" +# jose_title: "Music" +# jose_blurb: "Taking Off" +# retrostyle_title: "Illustration" +# retrostyle_blurb: "RetroStyle Games" + +# teachers: +# title: "CodeCombat: Info for Teachers" +# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." +# intro_2: "No experience required!" +# free_title: "How much does it cost?" +# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 USD per month for access to our other 140+ levels on our exclusive China servers." +# free_1: "There are 100+ FREE levels which cover every concept." +# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." +# teacher_subs_title: "Teachers get free subscriptions!" +# teacher_subs_1: "Please fill out our" +# teacher_subs_2: "Teacher Survey" +# teacher_subs_3: "to set up your subscription." +# sub_includes_title: "What is included in the subscription?" +# sub_includes_1: "In addition to the 100+ basic levels, students with a monthly subscription get access to these additional features:" +# sub_includes_2: "70+ practice levels" +# sub_includes_3: "Video tutorials" +# sub_includes_4: "Premium email support" +# sub_includes_5: "10 new heroes with unique skills to master" +# sub_includes_6: "3500 bonus gems every month" +# sub_includes_7: "Private Clans" +# monitor_progress_title: "How do I monitor student progress?" +# monitor_progress_1: "Student progress can be monitored by creating a" +# monitor_progress_2: "for your class." +# monitor_progress_3: "To add a student, send them the invite link for your Clan, which is on the" +# monitor_progress_4: "page." +# monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page." +# private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student." +# private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a" +# private_clans_3: "." +# who_for_title: "Who is CodeCombat for?" +# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." +# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." +# material_title: "How much material is there?" +# material_china: "Approximately 40 hours of gameplay spread over 170+ subscriber-only levels so far." +# material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content." +# concepts_title: "What concepts are covered?" +# how_much_title: "How much does a monthly subscription cost?" +# how_much_1: "A" +# how_much_2: "monthly subscription" +# how_much_3: "costs $9.99, and can be cancelled anytime." +# how_much_4: "Additionally, we provide discounts for larger groups:" +# how_much_5: "We accept discounted one-time purchases and yearly subscription purchases for groups, such as a class or school. Please contact" +# how_much_6: "for more details." +# more_info_title: "Where can I find more information?" +# more_info_1: "Our" +# more_info_2: "teachers forum" +# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat." +# sys_requirements_title: "System Requirements" +# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." +# sys_requirements_2: "CodeCombat is not supported on iPad yet." + +# teachers_survey: +# title: "Teacher Survey" +# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above." +# retrieving: "Retrieving information..." +# being_reviewed_1: "Your application for a free trial subscription is being" +# being_reviewed_2: "reviewed." +# approved_1: "Your application for a free trial subscription was" +# approved_2: "approved." +# approved_3: "Further instructions have been sent to" +# denied_1: "Your application for a free trial subscription has been" +# denied_2: "denied." +# contact_1: "Please contact" +# contact_2: "if you have further questions." +# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" +# description_2: "teachers" +# description_3: "page." +# description_4: "Please fill out this quick survey and we’ll email you setup instructions." +# email: "Email Address" +# school: "Name of School" +# location: "Name of City" +# age_students: "How old are your students?" +# under: "Under" +# other: "Other:" +# amount_students: "How many students do you teach?" +# hear_about: "How did you hear about CodeCombat?" +# fill_fields: "Please fill out all fields." +# thanks: "Thanks! We'll send you setup instructions shortly." versions: save_version_title: "存新版本" new_major_version: "新個重要版本" +# submitting_patch: "Submitting Patch..." cla_prefix: "想畀修改存起來,先頭要同意我裏個" cla_url: "貢獻者許可協議" cla_suffix: "。" cla_agree: "我同意" +# owner_approve: "An owner will need to approve it before your changes will become visible." contact: contact_us: "搭我裏聯繫" welcome: "我裏歡迎收到爾個信!用箇個表單寄信畀我裏。 " - contribute_prefix: "假使爾想貢獻眼物事,望我裏個 " - contribute_page: "貢獻頁面" - contribute_suffix: "!" forum_prefix: "假使爾想發佈弗管何物開放個物事, 好試試相" forum_page: "我裏個論壇" forum_suffix: "" +# faq_prefix: "There's also a" +# faq: "FAQ" +# subscribe_prefix: "If you need help figuring out a level, please" +# subscribe: "buy a CodeCombat subscription" +# subscribe_suffix: "and we'll be happy to help you with your code." +# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." +# screenshot_included: "Screenshot included." +# where_reply: "Where should we reply?" send: "提出意見" contact_candidate: "搭參選人聯繫" # Deprecated # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated @@ -450,12 +694,19 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio autosave: "改動自動存檔" me_tab: "我" picture_tab: "圖片" +# delete_account_tab: "Delete Your Account" +# wrong_email: "Wrong Email" +# wrong_password: "Wrong Password" # upload_picture: "Upload a picture" +# delete_this_account: "Delete this account permanently" +# god_mode: "God Mode" password_tab: "密碼" emails_tab: "電子信" admin: "管理" new_password: "新密碼" new_password_verify: "覈實" +# type_in_email: "Type in your email to confirm account deletion." +# type_in_password: "Also, type in your password." email_subscriptions: "郵箱校對" # email_subscriptions_none: "No Email Subscriptions." email_announcements: "通知" @@ -481,13 +732,12 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio job_profile_explanation: "爾好!填箇許訊息,我裏會用渠幫爾尋一份軟件開發個工作。" # sample_profile: "See a sample profile" # view_profile: "View Your Profile" - wizard_tab: "" - wizard_color: "巫師 衣裳 顏色" # keyboard_shortcuts: # keyboard_shortcuts: "Keyboard Shortcuts" # space: "Space" # enter: "Enter" +# press_enter: "press enter" # escape: "Escape" # shift: "Shift" # run_code: "Run current code." @@ -503,7 +753,6 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # toggle_pathfinding: "Toggle pathfinding overlay." # beautify: "Beautify your code by standardizing its formatting." # maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." # community: # main_title: "CodeCombat Community" @@ -523,19 +772,69 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # social_hipchat: "Chat with us in the public CodeCombat HipChat room" # contribute_to_the_project: "Contribute to the project" +# clans: +# clan: "Clan" +# clans: "Clans" +# new_name: "New clan name" +# new_description: "New clan description" +# make_private: "Make clan private" +# subs_only: "subscribers only" +# create_clan: "Create New Clan" +# private_preview: "Preview" +# public_clans: "Public Clans" +# my_clans: "My Clans" +# clan_name: "Clan Name" +# name: "Name" +# chieftain: "Chieftain" +# type: "Type" +# edit_clan_name: "Edit Clan Name" +# edit_clan_description: "Edit Clan Description" +# edit_name: "edit name" +# edit_description: "edit description" +# private: "(private)" +# summary: "Summary" +# average_level: "Average Level" +# average_achievements: "Average Achievements" +# delete_clan: "Delete Clan" +# leave_clan: "Leave Clan" +# join_clan: "Join Clan" +# invite_1: "Invite:" +# invite_2: "*Invite players to this Clan by sending them this link." +# members: "Members" +# progress: "Progress" +# not_started_1: "not started" +# started_1: "started" +# complete_1: "complete" +# exp_levels: "Expand levels" +# rem_hero: "Remove Hero" +# status: "Status" +# complete_2: "Complete" +# started_2: "Started" +# not_started_2: "Not Started" +# view_solution: "Click to view solution." +# latest_achievement: "Latest Achievement" +# playtime: "Playtime" +# last_played: "Last played" + classes: archmage_title: "大法師" archmage_title_description: "(寫代碼個人)" +# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" artisan_title: "泥水" artisan_title_description: "(做關造關人)" +# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." adventurer_title: "冒險家" adventurer_title_description: "(闖關測試人)" +# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." scribe_title: "文書" scribe_title_description: "(提醒編寫人)" +# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." diplomat_title: "外交官" diplomat_title_description: "(語言翻譯人)" +# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." ambassador_title: "使節" ambassador_title_description: "(用戶支持人)" +# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." editor: main_title: "CodeCombat 編寫器" @@ -543,18 +842,28 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio thang_title: "物事編寫器" level_title: "關編寫器" # achievement_title: "Achievement Editor" +# poll_title: "Poll Editor" back: "倒退" revert: "還原" revert_models: "還原模式" # pick_a_terrain: "Pick A Terrain" -# small: "Small" +# dungeon: "Dungeon" +# indoor: "Indoor" +# desert: "Desert" # grassy: "Grassy" +# mountain: "Mountain" +# glacier: "Glacier" +# small: "Small" +# large: "Large" fork_title: "派生新版本" fork_creating: "徠搭執行派生..." # generate_terrain: "Generate Terrain" more: "無數" wiki: "維基" live_chat: "上線白嗒" +# thang_main: "Main" +# thang_spritesheets: "Spritesheets" +# thang_colors: "Colors" level_some_options: "有解某條目?" level_tab_thangs: "物事" level_tab_scripts: "腳本" @@ -566,8 +875,13 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio level_tab_thangs_all: "所有" level_tab_thangs_conditions: "發動條件" level_tab_thangs_add: "加物事" +# level_tab_thangs_search: "Search thangs" +# add_components: "Add Components" +# component_configs: "Component Configurations" +# config_thang: "Double click to configure a thang" delete: "刪除" duplicate: "翻做" +# stop_duplicate: "Stop Duplicate" # rotate: "Rotate" level_settings_title: "設定" level_component_tab_title: "能界所有組件" @@ -592,33 +906,36 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # new_level_title_login: "Log In to Create a New Level" # new_achievement_title: "Create a New Achievement" # new_achievement_title_login: "Log In to Create a New Achievement" +# new_poll_title: "Create a New Poll" +# new_poll_title_login: "Log In to Create a New Poll" article_search_title: "徠箇搭尋物事" thang_search_title: "徠箇搭尋物事類型" level_search_title: "徠箇搭尋關" # achievement_search_title: "Search Achievements" +# poll_search_title: "Search Polls" read_only_warning2: "提醒:爾嘸處存編寫,朆登進之故" # no_achievements: "No achievements have been added for this level yet." # achievement_query_misc: "Key achievement off of miscellanea" # achievement_query_goals: "Key achievement off of level goals" # level_completion: "Level Completion" # pop_i18n: "Populate I18N" +# tasks: "Tasks" +# clear_storage: "Clear your local changes" +# add_system_title: "Add Systems to Level" +# done_adding: "Done Adding" article: edit_btn_preview: "試望" edit_article_title: "编辑提示" +# polls: +# priority: "Priority" + contribute: page_title: "貢獻" - character_classes_title: "貢獻者職業" - introduction_desc_intro: "我裏對 CodeCombat 個希望大險。" - introduction_desc_pref: "我裏希望所有個程序員聚隊趒來學學嬉戲,遊戲打打,讓各許人也見識到代碼個意思,展示社區最好個一面。我裏要弗得,也弗想單獨達成箇目標:爾要曉得, 讓 GitHub、Stack Overflow 搭 Linux 真當性本事是渠裏個用戶。爲達成箇目標," - introduction_desc_github_url: "我裏畀 CodeCombat 整個開源" - introduction_desc_suf: ",我裏也希望提供越多越好個方法讓爾參加箇項目,搭我裏聚隊造。" - introduction_desc_ending: "我裏希望爾也聚隊加進來!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy 搭 Matt" +# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" alert_account_message_intro: "爾好!" # alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "爾對遊戲圖像、界面設計、數據庫搭服務器運行、多人徠線、物理、聲音、遊戲引擎性能許感興趣噃?想做一個教別人編程個遊戲噃?空是爾有編程經驗,想開發 CodeCombat ,箇勿職業揀箇去。我裏候快活個,徠造“史上最讚個編程遊戲”個過程當中有爾個幫襯。" archmage_introduction: "做遊戲到,最激動個弗朝佩是拼合無數物事。圖像、音樂、實時網際通信、社交網絡,從底層數據庫管理到服務器運行維護,再到用戶界面個設計搭實現。造遊戲有無數事幹要捉拾,怪得空是爾有編程經驗,箇勿爾應該揀箇個職業。我裏猴高興來造“史上最讚個編程遊戲”條路裏搭爾佐隊。" class_attributes: "職業講明" archmage_attribute_1_pref: "瞭解" @@ -631,10 +948,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio join_desc_4: ",嚇我裏有較慢慢講!" join_url_email: "發信畀我裏" join_url_hipchat: " HipChat 白嗒間" - more_about_archmage: "瞭解怎兒當大法師" archmage_subscribe_desc: "用電子郵箱收新個編碼機會搭公告。" - artisan_summary_pref: "想做 CodeCombat 個關?人家攪個比我裏做個快無數啊!能界我裏個關編寫器還猴基本,做關做做還一粒煩難,還會有bug。只講爾有做關個想法,管簡單個for循環也是" - artisan_summary_suf: "許物事,箇個職業都搭爾猴相配個。" # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: ", then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -645,28 +959,21 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio artisan_join_step2: "做新關 搭打有個關數。" artisan_join_step3: "趒我裏個 HipChat 白嗒間來尋幫手。" artisan_join_step4: "畀爾個關發論壇讓別人畀爾評評。" - more_about_artisan: "瞭解怎兒當泥水" artisan_subscribe_desc: "用電子郵箱收關編寫器個新消息。" - adventurer_summary: "講講難聽點,爾佩是擋箭牌,還會傷殺甲險。我裏喫人試驗新關,再提提改進意見。做好個遊戲喫長久險個,嘸人頭一垡佩下落。空是爾熬得牢,人山板扎,箇職業註牢爾當。" # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" adventurer_forum_url: "我裏個論壇" adventurer_join_suf: "空是爾值得馨寧個方式得到通知, 箇勿註冊來!" - more_about_adventurer: "瞭解怎兒當冒險家" adventurer_subscribe_desc: "用電子郵箱收出新關消息。" - scribe_summary_pref: "CodeCombat 弗是單單整堆頭關數撞來,渠還是攪個人編程知識個來源。馨個話,個加個泥水人都好鏈接到詳細個文檔,畀攪遊戲個人學學,扣搭 " - scribe_summary_suf: "向。空是爾中意解釋編程個概念,箇勿箇職業爾猴合身個。" # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " scribe_introduction_url_mozilla: "Mozilla 開發人社區" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." contact_us_url: "聯繫我裏" scribe_join_description: "爾自己介紹記, 比方爾個編程經歷搭中意寫個物事,我裏會從搭開始畀爾瞭解!" - more_about_scribe: "瞭解怎兒當文書" scribe_subscribe_desc: "用電子郵箱收寫新文檔個通知。" - diplomat_summary: "無數國家弗講英語,不過許人也對 CodeCombat 興致頭高險!我裏需要有熱情個翻譯人,畀箇網站裏個文字儘話快速傳到全世界。假使爾想幫我裏傳播全球,箇勿箇職業適合爾。" diplomat_introduction_pref: "空是講我裏從" diplomat_launch_url: "十月個發佈" diplomat_introduction_suf: "裏向得到解某啓發:佩是全世界個人都對 CodeCombat 興致頭高險。我裏籠來一陣翻譯人,儘話快速畀網站裏個訊息翻譯成各地文字。空是爾對發佈新個內容有興趣,想讓爾個國土裏個人也來攪,快點趒來當外交官。" @@ -677,15 +984,12 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio diplomat_join_pref_github: "徠" diplomat_github_url: " GitHub " diplomat_join_suf_github: "尋着爾個語言文件 (吳語是: codecombat/app/locale/zh-WUU-HANT.coffee),徠線編寫渠,隨底提交一個合併請求。同時,選牢下底箇個多選框關注最新個國際化開發!" - more_about_diplomat: "瞭解如何當外交官" diplomat_subscribe_desc: "接收國際化開發搭翻譯情況個信" - ambassador_summary: "我裏要起一個社區,社區碰着囉唆事幹個時候,佩要支持人員上馬爻。我裏用 IRC、电子信、社交網站許各方平臺幫助攪箇遊戲個人熟悉箇遊戲。空是爾想幫人家參加進來,學編程,攪得高興,馨箇職業佩爾個。" ambassador_introduction: "箇是一個起頭個社區,爾也會變成我裏搭世界聯結個點。大家人都好用Olark隨底白嗒、發信、参加個人無數個社交網絡來認識瞭解討論我裏個遊戲。空是爾想幫助大家人快點加進來、攪攪意思、感受CodeCombat個脈搏、搭我裏聚隊,箇勿箇佩適合爾來做。" ambassador_attribute_1: "搭人家溝通本事好。識別得出攪個人碰着個問題,幫渠裏解決許問題。同時,搭我裏保持聯繫,及時反映攪個人哪搭中意弗中意、所望有解某!" ambassador_join_desc: "自己介紹記:爾解某做過爻?解某中意做?我裏從箇搭開始畀爾瞭解!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" - more_about_ambassador: "瞭解怎兒當使節" ambassador_subscribe_desc: "用電子郵箱收 支持系統個情況,搭多人遊戲方面個新情況。" changes_auto_save: "多選框勾起之後,改動會自動存檔。" diligent_scribes: "我裏勤力個文書:" @@ -740,11 +1044,13 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # fight: "Fight!" # watch_victory: "Watch your victory" # defeat_the: "Defeat the" +# tournament_started: ", started" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" # tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" # tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" +# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" # tournament_blurb_blog: "on our blog" # rules: "Rules" # winners: "Winners" @@ -763,6 +1069,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # no_achievements: "No Achievements earned yet." # favorite_prefix: "Favorite language is " # favorite_postfix: "." +# not_member_of_clans: "Not a member of any clans yet." # achievements: # last_earned: "Last Earned" @@ -785,6 +1092,35 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # account: # recently_played: "Recently Played" # no_recent_games: "No games played during the past two weeks." +# payments: "Payments" +# purchased: "Purchased" +# subscription: "Subscription" +# invoices: "Invoices" +# service_apple: "Apple" +# service_web: "Web" +# paid_on: "Paid On" +# service: "Service" +# price: "Price" +# gems: "Gems" +# active: "Active" +# subscribed: "Subscribed" +# unsubscribed: "Unsubscribed" +# active_until: "Active Until" +# cost: "Cost" +# next_payment: "Next Payment" +# card: "Card" +# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now." +# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!" + +# account_invoices: +# amount: "Amount in US dollars" +# declined: "Your card was declined" +# invalid_amount: "Please enter a US dollar amount." +# not_logged_in: "Log in or create an account to access invoices." +# pay: "Pay Invoice" +# purchasing: "Purchasing..." +# retrying: "Server error, retrying." +# success: "Successfully paid. Thanks!" loading_error: could_not_load: "讀取失敗" @@ -812,6 +1148,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio leaderboard: "排行榜" user_schema: "用戶模式" user_profile: "User Profile" +# patch: "Patch" patches: "補丁" # patched_model: "Source Document" # model: "Model" @@ -838,16 +1175,43 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # user_remarks: "User Remarks" # versions: "Versions" # items: "Items" +# hero: "Hero" # heroes: "Heroes" -# wizard: "Wizard" # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" # feedback: "Feedback" +# payment_info: "Payment Info" +# campaigns: "Campaigns" +# poll: "Poll" +# user_polls_record: "Poll Voting History" + +# concepts: +# advanced_strings: "Advanced Strings" +# algorithms: "Algorithms" +# arguments: "Arguments" +# arithmetic: "Arithmetic" +# arrays: "Arrays" +# basic_syntax: "Basic Syntax" +# boolean_logic: "Boolean Logic" +# break_statements: "Break Statements" +# classes: "Classes" +# for_loops: "For Loops" +# functions: "Functions" +# if_statements: "If Statements" +# input_handling: "Input Handling" +# math_operations: "Math Operations" +# object_literals: "Object Literals" +# strings: "Strings" +# variables: "Variables" +# vectors: "Vectors" +# while_loops: "Loops" +# recursion: "Recursion" # delta: # added: "Added" # modified: "Modified" +# not_modified: "Not Modified" # deleted: "Deleted" # moved_index: "Moved Index" # text_diff: "Text Diff" @@ -878,7 +1242,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio practices_title: "尊重最讚真做" practices_description: "箇是我裏對爾個保證,也佩是攪個人,徠法律用語裏向望起扣搭弗足相。" privacy_title: "隱私" - privacy_description: "我裏弗會畀爾個任何情報賣爻。我裏划算最後用招聘來得利,爾也放心,空是爾朆明确講肯,我裏弗會畀爾個私人情報賣畀有意個公司。" +# privacy_description: "We will not sell any of your personal information." security_title: "安全" security_description: "我裏儘話保證爾個個人隱私安全。當開源項目,管感爾都好檢查搭改進我裏自由開放個網站個安全。" email_title: "電子郵箱" @@ -887,12 +1251,6 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio email_description_suffix: "要勿我裏發畀爾個信裏向有鏈接,爾随低2都好改偏向設定要勿取消訂閱。" cost_title: "花銷" cost_description: "目前來講,CodeCombat 是全個免費個!我裏個大目標之一也是保持目前箇種方式,讓越多越好個人攪功還好,弗管是弗是生活裏向。空把天黯落來,我裏嘸數會畀訂一許內容收費,不過我裏能可弗馨妝。運道好個話,我裏好開公司,通過:" - recruitment_title: "招兵買馬" - recruitment_description_prefix: "來 CodeCombat 搭,爾會變做一個法力高強個“巫師”,弗單單徠遊戲裏,來生活當中也是。" - url_hire_programmers: "嘸人招程序員有得快爻," - recruitment_description_suffix: "怪得只講爾手藝讚起爻咦經過爾同意,我裏會畀爾最好個編碼成果畀講萬個僱主望,希望渠裏眼紅。渠裏解眼功夫鈿畀我裏,薪水發畀爾," - recruitment_description_italic: "“一大袋”" - recruitment_description_ending: "。箇網站也佩好一直免費,兩門進。划算佩馨寧。" copyrights_title: "版權搭許可" contributor_title: "貢獻者許可協議" contributor_description_prefix: "所有對本網站要勿 GitHub 代碼庫個努力都照我裏個" @@ -948,22 +1306,6 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # license: "license" # oreilly: "ebook of your choice" - wizard_settings: - title: "定做獻路人" - customize_avatar: "設定人頭照" - active: "開起" - color: "顏色" - group: "分類" - clothes: "衣裳" - trim: "格子" - cloud: "雲" - team: "隊" - spell: "魔法球" - boots: "鞋" - hue: "顏色" - saturation: "飽和度" - lightness: "亮度" - account_profile: # settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section. # edit_profile: "Edit Profile" diff --git a/app/models/Achievement.coffee b/app/models/Achievement.coffee index 4ee78a846..56632fef1 100644 --- a/app/models/Achievement.coffee +++ b/app/models/Achievement.coffee @@ -5,6 +5,7 @@ module.exports = class Achievement extends CocoModel @className: 'Achievement' @schema: require 'schemas/models/achievement' urlRoot: '/db/achievement' + editableByArtisans: true isRepeatable: -> @get('proportionalTo')? @@ -12,7 +13,7 @@ module.exports = class Achievement extends CocoModel getExpFunction: -> func = @get('function', true) return utils.functionCreators[func.kind](func.parameters) if func.kind of utils.functionCreators - + save: -> @populateI18N() super(arguments...) diff --git a/app/models/AnalyticsLogEvent.coffee b/app/models/AnalyticsLogEvent.coffee new file mode 100644 index 000000000..7b631ef3d --- /dev/null +++ b/app/models/AnalyticsLogEvent.coffee @@ -0,0 +1,6 @@ +CocoModel = require './CocoModel' + +module.exports = class AnalyticsLogEvent extends CocoModel + @className: 'AnalyticsLogEvent' + @schema: require 'schemas/models/analytics_log_event' + urlRoot: '/db/analytics.log.event' diff --git a/app/models/AnalyticsStripeInvoice.coffee b/app/models/AnalyticsStripeInvoice.coffee new file mode 100644 index 000000000..0da8f6627 --- /dev/null +++ b/app/models/AnalyticsStripeInvoice.coffee @@ -0,0 +1,6 @@ +CocoModel = require './CocoModel' + +module.exports = class AnalyticsStripeInvoice extends CocoModel + @className: 'AnalyticsStripeInvoice' + @schema: require 'schemas/models/analytics_stripe_invoice' + urlRoot: '/db/analytics.stripe.invoice' diff --git a/app/models/Article.coffee b/app/models/Article.coffee index 02e7fe80f..add63efc7 100644 --- a/app/models/Article.coffee +++ b/app/models/Article.coffee @@ -5,3 +5,4 @@ module.exports = class Article extends CocoModel @schema: require 'schemas/models/article' urlRoot: '/db/article' saveBackups: true + editableByArtisans: true diff --git a/app/models/Campaign.coffee b/app/models/Campaign.coffee new file mode 100644 index 000000000..54c53b33b --- /dev/null +++ b/app/models/Campaign.coffee @@ -0,0 +1,10 @@ +CocoModel = require './CocoModel' +schema = require 'schemas/models/campaign.schema' + +module.exports = class Campaign extends CocoModel + @className: 'Campaign' + @schema: schema + urlRoot: '/db/campaign' + saveBackups: true + @denormalizedLevelProperties: _.keys(_.omit(schema.properties.levels.additionalProperties.properties, ['unlocks', 'position', 'rewards'])) + @denormalizedCampaignProperties: ['name', 'i18n', 'slug'] diff --git a/app/models/Clan.coffee b/app/models/Clan.coffee new file mode 100644 index 000000000..23bcac753 --- /dev/null +++ b/app/models/Clan.coffee @@ -0,0 +1,7 @@ +CocoModel = require './CocoModel' +schema = require 'schemas/models/clan.schema' + +module.exports = class Clan extends CocoModel + @className: 'Clan' + @schema: schema + urlRoot: '/db/clan' diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index e68e36f36..94cffaea0 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -1,5 +1,6 @@ storage = require 'core/storage' deltasLib = require 'core/deltas' +locale = require 'locale/locale' class CocoModel extends Backbone.Model idAttribute: '_id' @@ -19,9 +20,14 @@ class CocoModel extends Backbone.Model @on 'error', @onError, @ @on 'add', @onLoaded, @ @saveBackup = _.debounce(@saveBackup, 500) - console.debug = console.log unless console.debug # Needed for IE10 and earlier + @usesVersions = @schema()?.properties?.version? + + backupKey: -> + if @usesVersions then @id else @id # + ':' + @attributes.__v # TODO: doesn't work because __v doesn't actually increment. #2061 + # if fixed, RevertModal will also need the fix setProjection: (project) -> + # TODO: ends up getting done twice, since the URL is modified and the @project is modified. So don't do this, just set project directly... (?) return if project is @project url = @getURL() url += '&project=' unless /project=/.test url @@ -41,16 +47,18 @@ class CocoModel extends Backbone.Model clone.set($.extend(true, {}, if withChanges then @attributes else @_revertAttributes)) clone - onError: -> + onError: (level, jqxhr) -> @loading = false @jqxhr = null + if jqxhr.status is 402 + Backbone.Mediator.publish 'level:subscription-required', {} onLoaded: -> @loaded = true @loading = false @jqxhr = null @loadFromBackup() - + getCreationDate: -> new Date(parseInt(@id.slice(0,8), 16)*1000) getNormalizedURL: -> "#{@urlRoot}/#{@id}" @@ -78,23 +86,23 @@ class CocoModel extends Backbone.Model thisTV4 = tv4.freshApi() thisTV4.addSchema('#', @schema()) thisTV4.addSchema('metaschema', require('schemas/metaschema')) - TreemaNode.utils.populateDefaults(clone, @schema(), thisTV4) + TreemaUtils.populateDefaults(clone, @schema(), thisTV4) @attributesWithDefaults = clone duration = new Date() - t0 console.debug "Populated defaults for #{@type()}#{if @attributes.name then ' ' + @attributes.name else ''} in #{duration}ms" if duration > 10 loadFromBackup: -> return unless @saveBackups - existing = storage.load @id + existing = storage.load @backupKey() if existing @set(existing, {silent: true}) - CocoModel.backedUp[@id] = @ + CocoModel.backedUp[@backupKey()] = @ saveBackup: -> @saveBackupNow() saveBackupNow: -> - storage.save(@id, @attributes) - CocoModel.backedUp[@id] = @ + storage.save(@backupKey(), @attributes) + CocoModel.backedUp[@backupKey()] = @ @backedUp = {} schema: -> return @constructor.schema @@ -116,11 +124,13 @@ class CocoModel extends Backbone.Model save: (attrs, options) -> options ?= {} + originalOptions = _.cloneDeep(options) options.headers ?= {} options.headers['X-Current-Path'] = document.location?.pathname ? 'unknown' success = options.success error = options.error options.success = (model, res) => + @retries = 0 @trigger 'save:success', @ success(@, res) if success @markToRevert() if @_revertAttributes @@ -128,6 +138,20 @@ class CocoModel extends Backbone.Model CocoModel.pollAchievements() options.success = options.error = null # So the callbacks can be garbage-collected. options.error = (model, res) => + if res.status is 0 + @retries ?= 0 + @retries += 1 + if @retries > 20 + msg = 'Your computer or our servers appear to be offline. Please try refreshing.' + noty text: msg, layout: 'center', type: 'error', killer: true + return + else + msg = $.i18n.t 'loading_error.connection_failure', defaultValue: 'Connection failed.' + try + noty text: msg, layout: 'center', type: 'error', killer: true, timeout: 3000 + catch notyError + console.warn "Couldn't even show noty error for", error, "because", notyError + return _.delay((f = => @save(attrs, originalOptions)), 3000) error(@, res) if error return unless @notyErrors errorMessage = "Error saving #{@get('name') ? @type()}" @@ -135,7 +159,7 @@ class CocoModel extends Backbone.Model console.warn errorMessage, res.responseJSON unless webkit?.messageHandlers # Don't show these notys on iPad try - noty text: "#{errorMessage}: #{res.status} #{res.statusText}", layout: 'topCenter', type: 'error', killer: false, timeout: 10000 + noty text: "#{errorMessage}: #{res.status} #{res.statusText}\n#{res.responseText}", layout: 'topCenter', type: 'error', killer: false, timeout: 10000 catch notyError console.warn "Couldn't even show noty error for", error, "because", notyError options.success = options.error = null # So the callbacks can be garbage-collected. @@ -163,6 +187,7 @@ class CocoModel extends Backbone.Model options ?= {} options.data ?= {} options.data.project = @project.join(',') if @project + #console.error @constructor.className, @, "fetching with cache?", options.cache, "options", options # Useful for debugging cached IE fetches @jqxhr = super(options) @loading = true @jqxhr @@ -182,7 +207,7 @@ class CocoModel extends Backbone.Model @clearBackup() clearBackup: -> - storage.remove @id + storage.remove @backupKey() hasLocalChanges: -> @_revertAttributes and not _.isEqual @attributes, @_revertAttributes @@ -213,6 +238,7 @@ class CocoModel extends Backbone.Model # actor is a User object actor ?= me return true if actor.isAdmin() + return true if actor.isArtisan() and @editableByArtisans for permission in (@get('permissions', true) ? []) if permission.target is 'public' or actor.get('_id') is permission.target return true if permission.access in ['owner', 'read'] @@ -223,6 +249,7 @@ class CocoModel extends Backbone.Model # actor is a User object actor ?= me return true if actor.isAdmin() + return true if actor.isArtisan() and @editableByArtisans for permission in (@get('permissions', true) ? []) if permission.target is 'public' or actor.get('_id') is permission.target return true if permission.access in ['owner', 'write'] @@ -335,19 +362,20 @@ class CocoModel extends Backbone.Model @pollAchievements: -> CocoCollection = require 'collections/CocoCollection' - Achievement = require 'models/Achievement' - + EarnedAchievement = require 'models/EarnedAchievement' + class NewAchievementCollection extends CocoCollection - model: Achievement + model: EarnedAchievement initialize: (me = require('core/auth').me) -> @url = "/db/user/#{me.id}/achievements?notified=false" achievements = new NewAchievementCollection achievements.fetch success: (collection) -> - me.fetch (success: -> Backbone.Mediator.publish('achievements:new', earnedAchievements: collection)) unless _.isEmpty(collection.models) + me.fetch (cache: false, success: -> Backbone.Mediator.publish('achievements:new', earnedAchievements: collection)) unless _.isEmpty(collection.models) error: -> console.error 'Miserably failed to fetch unnotified achievements', arguments + cache: false CocoModel.pollAchievements = _.debounce CocoModel.pollAchievements, 500 @@ -355,22 +383,44 @@ class CocoModel extends Backbone.Model #- Internationalization updateI18NCoverage: -> - i18nObjects = @findI18NObjects() - return unless i18nObjects.length - langCodeArrays = (_.keys(i18n) for i18n in i18nObjects) - @set('i18nCoverage', _.intersection(langCodeArrays...)) + langCodeArrays = [] + pathToData = {} - findI18NObjects: (data, results) -> - data ?= @attributes - results ?= [] + TreemaUtils.walk(@attributes, @schema(), null, (path, data, workingSchema) -> + # Store parent data for the next block... + if data?.i18n + pathToData[path] = data - if _.isPlainObject(data) or _.isArray(data) - for [key, value] in _.pairs data - if key is 'i18n' - results.push value - else if _.isPlainObject(value) or _.isArray(value) - @findI18NObjects(value, results) + if _.string.endsWith path, 'i18n' + i18n = data + + # grab the parent data + parentPath = path[0...-5] + parentData = pathToData[parentPath] + + # use it to determine what properties actually need to be translated + props = workingSchema.props or [] + props = (prop for prop in props when parentData[prop]) + #unless props.length + # console.log 'props is', props, 'path is', path, 'data is', data, 'parentData is', parentData, 'workingSchema is', workingSchema + # langCodeArrays.push _.without _.keys(locale), 'update' # Every language has covered a path with no properties to be translated. + # return + + return if 'additionalProperties' of i18n # Workaround for #2630: Programmable is weird + + # get a list of lang codes where its object has keys for every prop to be translated + coverage = _.filter(_.keys(i18n), (langCode) -> + translations = i18n[langCode] + _.all((translations[prop] for prop in props)) + ) + #console.log 'got coverage', coverage, 'for', path, props, workingSchema, parentData + langCodeArrays.push coverage + ) + + return unless langCodeArrays.length + # language codes that are covered for every i18n object are fully covered + overallCoverage = _.intersection(langCodeArrays...) + @set('i18nCoverage', overallCoverage) - return results module.exports = CocoModel diff --git a/app/models/EarnedAchievement.coffee b/app/models/EarnedAchievement.coffee index 680e6c7e2..653b34e66 100644 --- a/app/models/EarnedAchievement.coffee +++ b/app/models/EarnedAchievement.coffee @@ -5,3 +5,7 @@ module.exports = class EarnedAchievement extends CocoModel @className: 'EarnedAchievement' @schema: require 'schemas/models/earned_achievement' urlRoot: '/db/earned_achievement' + + save: -> + @unset('earnedRewards') if @get('earnedRewards') is null + super(arguments...) diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 17bc517b4..b32aa35b2 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -6,7 +6,11 @@ ThangType = require './ThangType' module.exports = class Level extends CocoModel @className: 'Level' @schema: require 'schemas/models/level' + @levels: + 'dungeons-of-kithgard': '5411cb3769152f1707be029c' + 'defense-of-plainswood': '541b67f71ccc8eaae19f3c62' urlRoot: '/db/level' + editableByArtisans: true serialize: (supermodel, session, otherSession, cached=false) -> o = @denormalize supermodel, session, otherSession # hot spot to optimize @@ -46,14 +50,14 @@ module.exports = class Level extends CocoModel denormalize: (supermodel, session, otherSession) -> o = $.extend true, {}, @attributes - if o.thangs and @get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] + if o.thangs and @get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] for levelThang in o.thangs @denormalizeThang(levelThang, supermodel, session, otherSession) o denormalizeThang: (levelThang, supermodel, session, otherSession) -> levelThang.components ?= [] - isHero = /Hero Placeholder/.test levelThang.id + isHero = /Hero Placeholder/.test(levelThang.id) and @get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] if isHero and otherSession # If it's a hero and there's another session, find the right session for it. # If there is no other session (playing against default code, or on single player), clone all placeholders. @@ -115,10 +119,16 @@ module.exports = class Level extends CocoModel else if placeholderConfig.programmableMethods # Take the ThangType default Programmable and merge level-specific Component config into it copy = $.extend true, {}, placeholderConfig + programmableProperties = levelThangComponent.config?.programmableProperties ? [] + copy.programmableProperties = _.union programmableProperties, copy.programmableProperties ? [] levelThangComponent.config = _.merge copy, levelThangComponent.config else if placeholderConfig.extraHUDProperties levelThangComponent.config ?= {} levelThangComponent.config.extraHUDProperties = _.union(levelThangComponent.config.extraHUDProperties ? [], placeholderConfig.extraHUDProperties) + else if placeholderConfig.voiceRange # Pull in voiceRange + levelThangComponent.config ?= {} + levelThangComponent.config.voiceRange = placeholderConfig.voiceRange + levelThangComponent.config.cooldown = placeholderConfig.cooldown if isHero if equips = _.find levelThang.components, {original: LevelComponent.EquipsID} diff --git a/app/models/LevelComponent.coffee b/app/models/LevelComponent.coffee index a54f130a7..1dd4264f7 100644 --- a/app/models/LevelComponent.coffee +++ b/app/models/LevelComponent.coffee @@ -14,7 +14,10 @@ module.exports = class LevelComponent extends CocoModel @PlansID: '524b7b517fc0f6d51900000d' @ProgrammableID: '524b7b5a7fc0f6d51900000e' @MovesID: '524b7b8c7fc0f6d519000013' + @MissileID: '524cc2593ea855e0ab000142' + @FindsPathsID: '52872b0ead92b98561000002' urlRoot: '/db/level.component' + editableByArtisans: true set: (key, val, options) -> if _.isObject key diff --git a/app/models/LevelSession.coffee b/app/models/LevelSession.coffee index 5d59d1bc2..0865d2e31 100644 --- a/app/models/LevelSession.coffee +++ b/app/models/LevelSession.coffee @@ -38,7 +38,7 @@ module.exports = class LevelSession extends CocoModel false isMultiplayer: -> - @get('team')? # Only multiplayer level sessions have teams defined + @get('submittedCodeLanguage')? and @get('team')? completed: -> @get('state')?.complete || false @@ -53,3 +53,37 @@ module.exports = class LevelSession extends CocoModel save: (attrs, options) -> return if @shouldAvoidCorruptData attrs super attrs, options + + increaseDifficulty: (callback) -> + state = @get('state') ? {} + state.difficulty = (state.difficulty ? 0) + 1 + delete state.lastUnsuccessfulSubmissionTime + @set 'state', state + @trigger 'change-difficulty' + @save null, success: callback + + timeUntilResubmit: -> + state = @get('state') ? {} + return 0 unless last = state.lastUnsuccessfulSubmissionTime + last = new Date(last) if _.isString last + # Wait at least this long before allowing submit button active again. + (last - new Date()) + 22 * 60 * 60 * 1000 + + recordScores: (scores, level) -> + state = @get 'state' + oldTopScores = state.topScores ? [] + newTopScores = [] + now = new Date() + for scoreType in level.get('scoreTypes') ? [] + oldTopScore = _.find oldTopScores, type: scoreType + newScore = scores[scoreType] + unless newScore? + newTopScores.push oldTopScore + continue + newScore *= -1 if scoreType in ['time', 'damage-taken'] # Make it so that higher is better + if not oldTopScore? or newScore > oldTopScore.score + newTopScores.push type: scoreType, date: now, score: newScore + else + newTopScores.push oldTopScore + state.topScores = newTopScores + @set 'state', state diff --git a/app/models/LevelSystem.coffee b/app/models/LevelSystem.coffee index a56775280..88fc473e2 100644 --- a/app/models/LevelSystem.coffee +++ b/app/models/LevelSystem.coffee @@ -5,6 +5,7 @@ module.exports = class LevelSystem extends CocoModel @className: 'LevelSystem' @schema: require 'schemas/models/level_system' urlRoot: '/db/level.system' + editableByArtisans: true set: (key, val, options) -> if _.isObject key diff --git a/app/models/Poll.coffee b/app/models/Poll.coffee new file mode 100644 index 000000000..4f72455dd --- /dev/null +++ b/app/models/Poll.coffee @@ -0,0 +1,48 @@ +CocoModel = require './CocoModel' +schema = require 'schemas/models/poll.schema' + +module.exports = class Poll extends CocoModel + @className: 'Poll' + @schema: schema + urlRoot: '/db/poll' + + applyDelta: (delta) -> + # Hackiest hacks ever, just manually mauling the delta (whose format I don't understand) to not overwrite votes and other languages' nested translations. + # One still must be careful about patches that accidentally delete keys from the top-level i18n object. + i18nDelta = {} + if delta.i18n + i18nDelta.i18n = $.extend true, {}, delta.i18n + for answerIndex, answerChanges of delta.answers ? {} + i18nDelta.answers ?= {} + if _.isArray answerChanges + i18nDelta.answers[answerIndex] ?= [] + for change in answerChanges + if _.isNumber change + pickedChange = change + else + pickedChange = $.extend true, {}, change + for key of pickedChange + answerIndexNum = parseInt(answerIndex.replace('_', ''), 10) + unless _.isNaN answerIndexNum + oldValue = @get('answers')[answerIndexNum][key] + isDeletion = _.string.startsWith answerIndex, '_' + isI18N = key is 'i18n' + if isI18N and not isDeletion + # Use the new change, but make sure we're not deleting any other languages' translations. + value = pickedChange[key] + for language, oldTranslations of oldValue ? {} + for translationKey, translationValue of oldTranslations ? {} + value[language] ?= {} + value[language][translationKey] ?= translationValue + else + value = oldValue + pickedChange[key] = value + i18nDelta.answers[answerIndex].push pickedChange + else + i18nDelta.answers[answerIndex] = answerChanges + if answerChanges?.votes + i18nDelta.answers[answerIndex] = _.omit answerChanges, 'votes' + + #console.log 'got delta', delta + #console.log 'got i18nDelta', i18nDelta + super i18nDelta diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee index 09fe9ed44..a932a87b6 100644 --- a/app/models/ThangType.coffee +++ b/app/models/ThangType.coffee @@ -12,21 +12,29 @@ module.exports = class ThangType extends CocoModel @heroes: captain: '529ec584c423d4e83b000014' knight: '529ffbf1cf1818f2be000001' - 'samurai': '53e12be0d042f23505c3023b' - 'ninja': '52fc0ed77e01835453bd8f6c' + samurai: '53e12be0d042f23505c3023b' + raider: '55527eb0b8abf4ba1fe9a107' + goliath: '' + guardian: '' + ninja: '52fc0ed77e01835453bd8f6c' 'forest-archer': '5466d4f2417c8b48a9811e87' trapper: '5466d449417c8b48a9811e83' + pixie: '' + assassin: '' librarian: '52fbf74b7e01835453bd8d8e' 'potion-master': '52e9adf7427172ae56002172' sorcerer: '52fd1524c7e6cf99160e7bc9' + necromancer: '55652fb3b9effa46a1f775fd' + 'dark-wizard': '' @heroClasses: - Warrior: ['captain', 'knight', 'samurai'] - Ranger: ['ninja', 'forest-archer', 'trapper'] - Wizard: ['librarian', 'potion-master', 'sorcerer'] + Warrior: ['captain', 'knight', 'samurai', 'raider', 'goliath', 'guardian'] + Ranger: ['ninja', 'forest-archer', 'trapper', 'pixie', 'assassin'] + Wizard: ['librarian', 'potion-master', 'sorcerer', 'necromancer', 'dark-wizard'] @items: 'simple-boots': '53e237bf53457600003e3f05' urlRoot: '/db/thang.type' building: {} + editableByArtisans: true initialize: -> super() @@ -233,7 +241,11 @@ module.exports = class ThangType extends CocoModel getPortraitStage: (spriteOptionsOrKey, size=100) -> canvas = $("<canvas width='#{size}' height='#{size}'></canvas>") - stage = new createjs.Stage(canvas[0]) + try + stage = new createjs.Stage(canvas[0]) + catch err + console.error "Error trying to create #{@get('name')} avatar stage:", err, "with window as", window + return null return stage unless @isFullyLoaded() key = spriteOptionsOrKey key = if _.isString(key) then key else @spriteSheetKey(@fillOptions(key)) @@ -282,8 +294,8 @@ module.exports = class ThangType extends CocoModel sprite = vectorParser.buildContainerFromStore(portrait.container) pt = portrait.positions?.registration - sprite.regX = pt?.x or 0 - sprite.regY = pt?.y or 0 + sprite.regX = pt?.x / scale or 0 + sprite.regY = pt?.y / scale or 0 sprite.scaleX = sprite.scaleY = scale * size / 100 stage.addChild(sprite) stage.update() @@ -291,7 +303,7 @@ module.exports = class ThangType extends CocoModel uploadGenericPortrait: (callback, src) -> src ?= @getPortraitSource() - return callback?() unless src and src.startsWith 'data:' + return callback?() unless src and _.string.startsWith src, 'data:' src = src.replace('data:image/png;base64,', '').replace(/\ /g, '+') body = filename: 'portrait.png' @@ -355,13 +367,22 @@ module.exports = class ThangType extends CocoModel else classSpecificScore = stat * 5 classAverage = @classStatAverages[prop][@get('heroClass')] - stats[prop] = Math.round(2 * ((classAverage - 2.5) + classSpecificScore / 2)) / 2 / 10 + stats[prop] = + relative: Math.round(2 * ((classAverage - 2.5) + classSpecificScore / 2)) / 2 / 10 + absolute: stat + pieces = ($.i18n.t "choose_hero.#{prop}_#{num}" for num in [1 .. 3]) + percent = Math.round(stat * 100) + '%' + className = $.i18n.t "general.#{_.string.slugify @get('heroClass')}" + stats[prop].description = [pieces[0], percent, pieces[1], className, pieces[2]].join ' ' minSpeed = 4 maxSpeed = 16 speedRange = maxSpeed - minSpeed speedPoints = rawNumbers.speed - minSpeed - stats.speed = Math.round(20 * speedPoints / speedRange) / 2 / 10 + stats.speed = + relative: Math.round(20 * speedPoints / speedRange) / 2 / 10 + absolute: rawNumbers.speed + description: "#{$.i18n.t 'choose_hero.speed_1'} #{rawNumbers.speed} #{$.i18n.t 'choose_hero.speed_2'}" stats.skills = (_.string.titleize(_.string.humanize(skill)) for skill in programmableConfig.programmableProperties when skill isnt 'say') @@ -412,12 +433,15 @@ module.exports = class ThangType extends CocoModel throwDamage: 'attack' throwRange: 'range' bashDamage: 'attack' + backstabDamage: 'backstab' }[name] if i18nKey name = $.i18n.t 'choose_hero.' + i18nKey + matchedShortName = true else name = _.string.humanize name + matchedShortName = false format = '' format = 'm' if /(range|radius|distance|vision)$/i.test name @@ -436,7 +460,7 @@ module.exports = class ThangType extends CocoModel display.push "x#{modifiers.factor}" if modifiers.factor? and modifiers.factor isnt 1 display = display.join ', ' display = display.replace /9001m?/, 'Infinity' - name: name, display: display + name: name, display: display, matchedShortName: matchedShortName isSilhouettedItem: -> return console.error "Trying to determine whether #{@get('name')} should be a silhouetted item, but it has no gem cost." unless @get('gems') or @get('tier') diff --git a/app/models/TrialRequest.coffee b/app/models/TrialRequest.coffee new file mode 100644 index 000000000..5ea9cbeb4 --- /dev/null +++ b/app/models/TrialRequest.coffee @@ -0,0 +1,7 @@ +CocoModel = require './CocoModel' +schema = require 'schemas/models/trial_request.schema' + +module.exports = class TrialRequest extends CocoModel + @className: 'TrialRequest' + @schema: schema + urlRoot: '/db/trial.request' diff --git a/app/models/User.coffee b/app/models/User.coffee index 3fec2a995..e25fc67b8 100644 --- a/app/models/User.coffee +++ b/app/models/User.coffee @@ -3,6 +3,7 @@ cache = {} CocoModel = require './CocoModel' util = require 'core/utils' ThangType = require './ThangType' +Level = require './Level' module.exports = class User extends CocoModel @className: 'User' @@ -10,11 +11,9 @@ module.exports = class User extends CocoModel urlRoot: '/db/user' notyErrors: false - onLoaded: -> - CocoModel.pollAchievements() # Check for achievements on login - super arguments... - isAdmin: -> 'admin' in @get('permissions', true) + isArtisan: -> 'artisan' in @get('permissions', true) + isInGodMode: -> 'godmode' in @get('permissions', true) isAnonymous: -> @get('anonymous', true) displayName: -> @get('name', true) @@ -36,6 +35,7 @@ module.exports = class User extends CocoModel @getUnconflictedName: (name, done) -> $.ajax "/auth/name/#{name}", + cache: false success: (data) -> done data.name statusCode: 409: (data) -> response = JSON.parse data.responseText @@ -74,24 +74,27 @@ module.exports = class User extends CocoModel return level if tierThreshold >= tier level: -> - User.levelFromExp(@get('points')) + totalPoint = @get('points') + totalPoint = totalPoint + 1000000 if me.isInGodMode() + User.levelFromExp(totalPoint) tier: -> User.tierFromLevel @level() gems: -> gemsEarned = @get('earned')?.gems ? 0 + gemsEarned = gemsEarned + 100000 if me.isInGodMode() gemsPurchased = @get('purchased')?.gems ? 0 gemsSpent = @get('spent') ? 0 - gemsEarned + gemsPurchased - gemsSpent + Math.floor gemsEarned + gemsPurchased - gemsSpent heroes: -> heroes = (me.get('purchased')?.heroes ? []).concat([ThangType.heroes.captain, ThangType.heroes.knight]) #heroes = _.values ThangType.heroes if me.isAdmin() heroes items: -> (me.get('earned')?.items ? []).concat(me.get('purchased')?.items ? []).concat([ThangType.items['simple-boots']]) - levels: -> (me.get('earned')?.levels ? []).concat(me.get('purchased')?.levels ? []) - ownsHero: (heroOriginal) -> heroOriginal in @heroes() + levels: -> (me.get('earned')?.levels ? []).concat(me.get('purchased')?.levels ? []).concat(Level.levels['dungeons-of-kithgard']) + ownsHero: (heroOriginal) -> me.isInGodMode() || heroOriginal in @heroes() ownsItem: (itemOriginal) -> itemOriginal in @items() ownsLevel: (levelOriginal) -> levelOriginal in @levels() @@ -102,27 +105,47 @@ module.exports = class User extends CocoModel myHeroClasses.push heroClass for heroClass, heroSlugs of ThangType.heroClasses when _.intersection(myHeroSlugs, heroSlugs).length myHeroClasses - getBranchingGroup: -> - return @branchingGroup if @branchingGroup + getAnnouncesActionAudioGroup: -> + return @announcesActionAudioGroup if @announcesActionAudioGroup group = me.get('testGroupNumber') % 4 - @branchingGroup = switch group - when 0 then 'no-practice' - when 1 then 'all-practice' - when 2 then 'choice-explicit' - when 3 then 'choice-implicit' - @branchingGroup = 'choice-explicit' if me.isAdmin() - application.tracker.identify branchingGroup: @branchingGroup unless me.isAdmin() - @branchingGroup + @announcesActionAudioGroup = switch group + when 0 then 'all-audio' + when 1 then 'no-audio' + when 2 then 'just-take-damage' + when 3 then 'without-take-damage' + @announcesActionAudioGroup = 'all-audio' if me.isAdmin() + application.tracker.identify announcesActionAudioGroup: @announcesActionAudioGroup unless me.isAdmin() + @announcesActionAudioGroup - getGemPromptGroup: -> - return @gemPromptGroup if @gemPromptGroup + # Signs and Portents was receiving updates after test started, and also had a big bug on March 4, so just look at test from March 5 on. + # ... and stopped working well until another update on March 10, so maybe March 11+... + # ... and another round, and then basically it just isn't completing well, so we pause the test until we can fix it. + getFourthLevelGroup: -> + return 'forgetful-gemsmith' + return @fourthLevelGroup if @fourthLevelGroup group = me.get('testGroupNumber') % 8 - @gemPromptGroup = switch group - when 0, 1, 2, 3 then 'prompt' - when 4, 5, 6, 7 then 'no-prompt' - @gemPromptGroup = 'prompt' if me.isAdmin() - application.tracker.identify gemPromptGroup: @gemPromptGroup unless me.isAdmin() - @gemPromptGroup + @fourthLevelGroup = switch group + when 0, 1, 2, 3 then 'signs-and-portents' + when 4, 5, 6, 7 then 'forgetful-gemsmith' + @fourthLevelGroup = 'signs-and-portents' if me.isAdmin() + application.tracker.identify fourthLevelGroup: @fourthLevelGroup unless me.isAdmin() + @fourthLevelGroup + + getVideoTutorialStylesIndex: (numVideos=0)-> + # A/B Testing video tutorial styles + # Not a constant number of videos available (e.g. could be 0, 1, 3, or 4 currently) + return 0 unless numVideos > 0 + return me.get('testGroupNumber') % numVideos + + isPremium: -> + return true if me.isInGodMode() + return true if me.isAdmin() + return false unless stripe = @get('stripe') + return true if stripe.sponsorID + return true if stripe.subscriptionID + return true if stripe.free is true + return true if _.isString(stripe.free) and new Date() < new Date(stripe.free) + return false tiersByLevel = [-1, 0, 0.05, 0.14, 0.18, 0.32, 0.41, 0.5, 0.64, 0.82, 0.91, 1.04, 1.22, 1.35, 1.48, 1.65, 1.78, 1.96, 2.1, 2.24, 2.38, 2.55, 2.69, 2.86, 3.03, 3.16, 3.29, 3.42, 3.58, 3.74, 3.89, 4.04, 4.19, 4.32, 4.47, 4.64, 4.79, 4.96, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10.5, 11, 11.5, 12, 12.5, 13, 13.5, 14, 14.5, 15 diff --git a/app/models/UserPollsRecord.coffee b/app/models/UserPollsRecord.coffee new file mode 100644 index 000000000..c8f1113fc --- /dev/null +++ b/app/models/UserPollsRecord.coffee @@ -0,0 +1,7 @@ +CocoModel = require './CocoModel' +schema = require 'schemas/models/user-polls-record.schema' + +module.exports = class UserPollsRecord extends CocoModel + @className: 'UserPollsRecord' + @schema: schema + urlRoot: '/db/user.polls.record' diff --git a/app/schemas/models/achievement.coffee b/app/schemas/models/achievement.coffee index 289b5e2bd..fdf783754 100644 --- a/app/schemas/models/achievement.coffee +++ b/app/schemas/models/achievement.coffee @@ -66,7 +66,7 @@ _.extend AchievementSchema.properties, type: 'object' description: 'Function that gives total experience for X amount achieved' properties: - kind: {enum: ['linear', 'logarithmic', 'quadratic'] } + kind: {enum: ['linear', 'logarithmic', 'quadratic', 'pow'] } parameters: type: 'object' default: { a: 1, b: 0, c: 0 } @@ -91,5 +91,6 @@ AchievementSchema.definitions = {} AchievementSchema.definitions['mongoQueryOperator'] = MongoQueryOperatorSchema AchievementSchema.definitions['mongoFindQuery'] = MongoFindQuerySchema c.extendTranslationCoverageProperties AchievementSchema +c.extendPatchableProperties AchievementSchema module.exports = AchievementSchema diff --git a/app/schemas/models/analytics_log_event.coffee b/app/schemas/models/analytics_log_event.coffee new file mode 100644 index 000000000..d3601a731 --- /dev/null +++ b/app/schemas/models/analytics_log_event.coffee @@ -0,0 +1,20 @@ +c = require './../schemas' + +AnalyticsLogEventSchema = c.object { + title: 'Analytics Log Event' + description: 'Analytics event logs.' +} + +_.extend AnalyticsLogEventSchema.properties, + u: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}]) + e: {type: 'integer'} + p: {type: 'object'} + + # TODO: Remove these legacy properties after we stop querying for them (probably 30 days, ~2/16/15) + user: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}]) + event: {type: 'string'} + properties: {type: 'object'} + +c.extendBasicProperties AnalyticsLogEventSchema, 'analytics.log.event' + +module.exports = AnalyticsLogEventSchema diff --git a/app/schemas/models/analytics_perday.coffee b/app/schemas/models/analytics_perday.coffee new file mode 100644 index 000000000..77528dc27 --- /dev/null +++ b/app/schemas/models/analytics_perday.coffee @@ -0,0 +1,18 @@ +c = require './../schemas' + +AnalyticsPerDaySchema = c.object { + title: 'Analytics per-day data' + description: 'Analytics data aggregated into per-day chunks.' +} + +_.extend AnalyticsPerDaySchema.properties, + d: {type: 'string'} # yyyymmdd day, e.g. '20150123' + e: {type: 'integer'} # event (analytics string ID from analytics.strings) + l: {type: 'integer'} # level (analytics ID from analytics.strings) + f: {type: 'integer'} # filter (analytics ID from analytics.strings) + fv: {type: 'integer'} # filter value (analytics ID from analytics.strings) + c: {type: 'integer'} # count + +c.extendBasicProperties AnalyticsPerDaySchema, 'analytics.perday' + +module.exports = AnalyticsPerDaySchema diff --git a/app/schemas/models/analytics_string.coffee b/app/schemas/models/analytics_string.coffee new file mode 100644 index 000000000..d201995c0 --- /dev/null +++ b/app/schemas/models/analytics_string.coffee @@ -0,0 +1,13 @@ +c = require './../schemas' + +AnalyticsStringSchema = c.object { + title: 'Analytics String' + description: 'Maps strings to number IDs for improved performance.' +} + +_.extend AnalyticsStringSchema.properties, + v: {type: 'string'} # value + +c.extendBasicProperties AnalyticsStringSchema, 'analytics.string' + +module.exports = AnalyticsStringSchema diff --git a/app/schemas/models/analytics_stripe_invoice.coffee b/app/schemas/models/analytics_stripe_invoice.coffee new file mode 100644 index 000000000..9cd49f067 --- /dev/null +++ b/app/schemas/models/analytics_stripe_invoice.coffee @@ -0,0 +1,14 @@ +c = require './../schemas' + +AnalyticsStripeInvoiceSchema = c.object { + title: 'Analytics Stripe Invoice' +} + +_.extend AnalyticsStripeInvoiceSchema.properties, + _id: {type: 'string'} + date: {type: 'integer'} + properties: {type: 'object'} + +c.extendBasicProperties AnalyticsStripeInvoiceSchema, 'analytics.stripe.invoice' + +module.exports = AnalyticsStripeInvoiceSchema diff --git a/app/schemas/models/analytics_users_active.coffee b/app/schemas/models/analytics_users_active.coffee new file mode 100644 index 000000000..f3c6e6a80 --- /dev/null +++ b/app/schemas/models/analytics_users_active.coffee @@ -0,0 +1,16 @@ +c = require './../schemas' + +AnalyticsUsersActiveSchema = c.object { + title: 'Analytics Users Active' + description: 'Active users data.' +} + +_.extend AnalyticsUsersActiveSchema.properties, + creator: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}]) + created: c.date({title: 'Created', readOnly: true}) + + event: {type: 'string'} + +c.extendBasicProperties AnalyticsUsersActiveSchema, 'analytics.users.active' + +module.exports = AnalyticsUsersActiveSchema diff --git a/app/schemas/models/article.coffee b/app/schemas/models/article.coffee index eccab57b3..c8452e1a7 100644 --- a/app/schemas/models/article.coffee +++ b/app/schemas/models/article.coffee @@ -9,6 +9,7 @@ ArticleSchema.properties.i18n = {type: 'object', title: 'i18n', format: 'i18n', c.extendBasicProperties ArticleSchema, 'article' c.extendSearchableProperties ArticleSchema c.extendVersionedProperties ArticleSchema, 'article' +c.extendTranslationCoverageProperties ArticleSchema c.extendPatchableProperties ArticleSchema module.exports = ArticleSchema diff --git a/app/schemas/models/campaign.schema.coffee b/app/schemas/models/campaign.schema.coffee new file mode 100644 index 000000000..5f6f45bfd --- /dev/null +++ b/app/schemas/models/campaign.schema.coffee @@ -0,0 +1,132 @@ +c = require './../schemas' + +CampaignSchema = c.object() +c.extendNamedProperties CampaignSchema # name first + +_.extend CampaignSchema.properties, { + i18n: {type: 'object', title: 'i18n', format: 'i18n', props: ['name', 'fullName', 'description']} + fullName: { type: 'string', title: 'Full Name', description: 'Ex.: "Kithgard Dungeon"' } + description: { type: 'string', format: 'string', description: 'How long it takes and what players learn.' } + + ambientSound: c.object {}, + mp3: { type: 'string', format: 'sound-file' } + ogg: { type: 'string', format: 'sound-file' } + + backgroundImage: c.array {}, { + type: 'object' + additionalProperties: false + properties: { + image: { type: 'string', format: 'image-file' } + width: { type: 'number' } + } + } + backgroundColor: { type: 'string' } + backgroundColorTransparent: { type: 'string' } + + adjacentCampaigns: { type: 'object', format: 'campaigns', additionalProperties: { + title: 'Campaign' + type: 'object' + format: 'campaign' + properties: { + #- denormalized from other Campaigns, either updated automatically or fetched dynamically + id: { type: 'string', format: 'hidden' } + name: { type: 'string', format: 'hidden' } + description: { type: 'string', format: 'hidden' } + i18n: { type: 'object', format: 'hidden' } + slug: { type: 'string', format: 'hidden' } + + #- normal properties + position: c.point2d() + rotation: { type: 'number', format: 'degrees' } + color: { type: 'string' } + showIfUnlocked: { type: 'string', links: [{rel: 'db', href: '/db/level/{($)}/version'}], format: 'latest-version-original-reference' } + } + }} + + levels: { type: 'object', format: 'levels', additionalProperties: { + title: 'Level' + type: 'object' + format: 'level' + additionalProperties: false + + # key is the original property + properties: { + #- denormalized from Level + name: { type: 'string', format: 'hidden' } + description: { type: 'string', format: 'hidden' } + i18n: { type: 'object', format: 'hidden' } + requiresSubscription: { type: 'boolean' } + replayable: { type: 'boolean' } + type: {'enum': ['ladder', 'ladder-tutorial', 'hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder']} + slug: { type: 'string', format: 'hidden' } + original: { type: 'string', format: 'hidden' } + adventurer: { type: 'boolean' } + practice: { type: 'boolean' } + adminOnly: { type: 'boolean' } + disableSpaces: { type: ['boolean','number'] } + hidesSubmitUntilRun: { type: 'boolean' } + hidesPlayButton: { type: 'boolean' } + hidesRunShortcut: { type: 'boolean' } + hidesHUD: { type: 'boolean' } + hidesSay: { type: 'boolean' } + hidesCodeToolbar: { type: 'boolean' } + hidesRealTimePlayback: { type: 'boolean' } + backspaceThrottle: { type: 'boolean' } + lockDefaultCode: { type: ['boolean','number'] } + moveRightLoopSnippet: { type: 'boolean' } + realTimeSpeedFactor: { type: 'number' } + autocompleteFontSizePx: { type: 'number' } + + requiredCode: c.array {}, { + type: 'string' + } + suspectCode: c.array {}, { + type: 'object' + properties: { + name: { type: 'string' } + pattern: { type: 'string' } + } + } + + requiredGear: { type: 'object', additionalProperties: { + type: 'array' + items: { type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference' } + }} + restrictedGear: { type: 'object', additionalProperties: { + type: 'array' + items: { type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference' } + }} + allowedHeroes: { type: 'array', items: { + type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference' + }} + + #- denormalized from Achievements + rewards: { type: 'array', items: { + type: 'object' + additionalProperties: false + properties: + achievement: { type: 'string', links: [{rel: 'db', href: '/db/achievement/{{$}}'}], format: 'achievement' } + item: { type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference' } + hero: { type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference' } + level: { type: 'string', links: [{rel: 'db', href: '/db/level/{($)}/version'}], format: 'latest-version-original-reference' } + type: { enum: ['heroes', 'items', 'levels'] } + }} + + campaign: c.shortString title: 'Campaign', description: 'Which campaign this level is part of (like "desert").', format: 'hidden' # Automatically set by campaign editor. + + tasks: c.array {title: 'Tasks', description: 'Tasks to be completed for this level.'}, c.task + concepts: c.array {title: 'Programming Concepts', description: 'Which programming concepts this level covers.'}, c.concept + + #- normal properties + position: c.point2d() + } + + }} +} + + +c.extendBasicProperties CampaignSchema, 'campaign' +c.extendTranslationCoverageProperties CampaignSchema +c.extendPatchableProperties CampaignSchema + +module.exports = CampaignSchema diff --git a/app/schemas/models/clan.schema.coffee b/app/schemas/models/clan.schema.coffee new file mode 100644 index 000000000..acdc17cd9 --- /dev/null +++ b/app/schemas/models/clan.schema.coffee @@ -0,0 +1,21 @@ +c = require './../schemas' + +# TODO: Require name to be non-empty + +ClanSchema = c.object {title: 'Clan', required: ['name', 'type']} +c.extendNamedProperties ClanSchema # name first + +_.extend ClanSchema.properties, + description: {type: 'string'} + members: c.array {title: 'Members'}, c.objectId() + ownerID: c.objectId() + type: {type: 'string', 'enum': ['public', 'private'], description: 'Controls clan general visibility.'} + dashboardType: {type: 'string', 'enum': ['basic', 'premium']} + +c.extendBasicProperties ClanSchema, 'Clan' + +# Do we need these? +# c.extendSearchableProperties ClanSchema +# c.extendPermissionsProperties ClanSchema + +module.exports = ClanSchema diff --git a/app/schemas/models/earned_achievement.coffee b/app/schemas/models/earned_achievement.coffee index a78940d0d..f4dd8eb50 100644 --- a/app/schemas/models/earned_achievement.coffee +++ b/app/schemas/models/earned_achievement.coffee @@ -26,8 +26,8 @@ module.exports = collection: type: 'string' triggeredBy: c.objectId() achievementName: type: 'string' - created: type: 'date' - changed: type: 'date' + created: type: ['date', 'string', 'number'] + changed: type: ['date', 'string', 'number'] # TODO: migrate timestamps and Date objects all to ISO strings achievedAmount: type: 'number' earnedPoints: type: 'number' previouslyAchievedAmount: {type: 'number'} diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee index b9c72fff4..291904e25 100644 --- a/app/schemas/models/level.coffee +++ b/app/schemas/models/level.coffee @@ -1,6 +1,50 @@ c = require './../schemas' ThangComponentSchema = require './thang_component' +defaultTasks = [ + 'Name the level.' + 'Create a Referee stub, if needed.' + 'Build the level.' + 'Set up goals.' + 'Choose the Existence System lifespan and frame rate.' + 'Choose the UI System paths and coordinate hover if needed.' + 'Choose the AI System pathfinding and Vision System line of sight.' + 'Write the sample code.' + + 'Do basic set decoration.' + 'Adjust script camera bounds.' + 'Choose music file in Introduction script.' + + 'Add to a campaign.' + 'Publish.' + 'Choose level options like required/restricted gear.' + 'Create achievements, including unlocking next level.' + 'Choose leaderboard score types.' + + 'Playtest with a slow/tough hero.' + 'Playtest with a fast/weak hero.' + 'Playtest with a couple random seeds.' + 'Make sure the level ends promptly on success and failure.' + 'Remove/simplify unnecessary doodad collision.' + 'Release to adventurers via MailChimp.' + + 'Write the description.' + 'Translate the sample code comments.' + 'Add Io/Clojure/Lua/CoffeeScript.' + 'Write the guide.' + 'Write a loading tip, if needed.' + 'Click the Populate i18n button.' + 'Add programming concepts covered.' + + 'Mark whether it requires a subscription.' + 'Release to everyone via MailChimp.' + + 'Check completion/engagement/problem analytics.' + 'Do any custom scripting, if needed.' + 'Do thorough set decoration.' + 'Add a walkthrough video.' +] + SpecificArticleSchema = c.object() c.extendNamedProperties SpecificArticleSchema # name first SpecificArticleSchema.properties.body = {type: 'string', title: 'Content', description: 'The body content of the article, in Markdown.', format: 'markdown'} @@ -34,7 +78,7 @@ GoalSchema = c.object {title: 'Goal', description: 'A goal that the player can a optional: {title: 'Optional', description: 'Optional goals do not need to be completed for overallStatus to be success.', type: 'boolean'} team: c.shortString(title: 'Team', description: 'Name of the team this goal is for, if it is not for all of the playable teams.') killThangs: c.array {title: 'Kill Thangs', description: 'A list of Thang IDs the player should kill, or team names.', uniqueItems: true, minItems: 1, 'default': ['ogres']}, thang - saveThangs: c.array {title: 'Save Thangs', description: 'A list of Thang IDs the player should save, or team names', uniqueItems: true, minItems: 1, 'default': ['humans']}, thang + saveThangs: c.array {title: 'Save Thangs', description: 'A list of Thang IDs the player should save, or team names', uniqueItems: true, minItems: 1, 'default': ['Hero Placeholder']}, thang getToLocations: c.object {title: 'Get To Locations', description: 'Will be set off when any of the \"who\" touch any of the \"targets\"', required: ['who', 'targets']}, who: c.array {title: 'Who', description: 'The Thangs who must get to the target locations.', minItems: 1}, thang targets: c.array {title: 'Targets', description: 'The target locations to which the Thangs must get.', minItems: 1}, thang @@ -218,8 +262,9 @@ LevelSchema = c.object { type: 'hero' goals: [ {id: 'ogres-die', name: 'Ogres must die.', killThangs: ['ogres'], worldEndsAfter: 3} - {id: 'humans-survive', name: 'Humans must survive.', saveThangs: ['humans'], howMany: 1, worldEndsAfter: 3, hiddenGoal: true} + {id: 'humans-survive', name: 'Your hero must survive.', saveThangs: ['Hero Placeholder'], howMany: 1, worldEndsAfter: 3, hiddenGoal: true} ] + concepts: ['basic_syntax'] } c.extendNamedProperties LevelSchema # let's have the name be the first property _.extend LevelSchema.properties, @@ -248,9 +293,61 @@ _.extend LevelSchema.properties, icon: {type: 'string', format: 'image-file', title: 'Icon'} banner: {type: 'string', format: 'image-file', title: 'Banner'} goals: c.array {title: 'Goals', description: 'An array of goals which are visible to the player and can trigger scripts.'}, GoalSchema - type: c.shortString(title: 'Type', description: 'What kind of level this is.', 'enum': ['campaign', 'ladder', 'ladder-tutorial', 'hero', 'hero-ladder', 'hero-coop']) + type: c.shortString(title: 'Type', description: 'What kind of level this is.', 'enum': ['campaign', 'ladder', 'ladder-tutorial', 'hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder']) terrain: c.terrainString showsGuide: c.shortString(title: 'Shows Guide', description: 'If the guide is shown at the beginning of the level.', 'enum': ['first-time', 'always']) + requiresSubscription: {title: 'Requires Subscription', description: 'Whether this level is available to subscribers only.', type: 'boolean'} + tasks: c.array {title: 'Tasks', description: 'Tasks to be completed for this level.', default: (name: t for t in defaultTasks)}, c.task + helpVideos: c.array {title: 'Help Videos'}, c.object {default: {style: 'eccentric', url: '', free: false}}, + style: c.shortString title: 'Style', description: 'Like: original, eccentric, scripted, edited, etc.' + free: {type: 'boolean', title: 'Free', description: 'Whether this video is freely available to all players without a subscription.'} + url: c.url {title: 'URL', description: 'Link to the video on Vimeo.'} + replayable: {type: 'boolean', title: 'Replayable', description: 'Whether this (hero) level infinitely scales up its difficulty and can be beaten over and over for greater rewards.'} + buildTime: {type: 'number', description: 'How long it has taken to build this level.'} + + # Admin flags + adventurer: { type: 'boolean' } + practice: { type: 'boolean' } + adminOnly: { type: 'boolean' } + disableSpaces: { type: ['boolean','integer'] } + hidesSubmitUntilRun: { type: 'boolean' } + hidesPlayButton: { type: 'boolean' } + hidesRunShortcut: { type: 'boolean' } + hidesHUD: { type: 'boolean' } + hidesSay: { type: 'boolean' } + hidesCodeToolbar: { type: 'boolean' } + hidesRealTimePlayback: { type: 'boolean' } + backspaceThrottle: { type: 'boolean' } + lockDefaultCode: { type: ['boolean','integer'] } + moveRightLoopSnippet: { type: 'boolean' } + realTimeSpeedFactor: { type: 'number' } + autocompleteFontSizePx: { type: 'number' } + requiredCode: c.array {}, { + type: 'string' + } + suspectCode: c.array {}, { + type: 'object' + properties: { + name: { type: 'string' } + pattern: { type: 'string' } + } + } + requiredGear: { type: 'object', additionalProperties: { + type: 'array' + items: { type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference' } + }} + restrictedGear: { type: 'object', additionalProperties: { + type: 'array' + items: { type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference' } + }} + allowedHeroes: { type: 'array', items: { + type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference' + }} + campaign: c.shortString title: 'Campaign', description: 'Which campaign this level is part of (like "desert").', format: 'hidden' # Automatically set by campaign editor. + scoreTypes: c.array {title: 'Score Types', description: 'What metric to show leaderboards for.', uniqueItems: true}, + c.shortString(title: 'Score Type', 'enum': ['time', 'damage-taken', 'damage-dealt', 'gold-collected', 'difficulty']) # TODO: good version of LoC; total gear value. + concepts: c.array {title: 'Programming Concepts', description: 'Which programming concepts this level covers.', uniqueItems: true}, c.concept + c.extendBasicProperties LevelSchema, 'level' c.extendSearchableProperties LevelSchema diff --git a/app/schemas/models/level_session.coffee b/app/schemas/models/level_session.coffee index 259fe9467..d7b360987 100644 --- a/app/schemas/models/level_session.coffee +++ b/app/schemas/models/level_session.coffee @@ -29,6 +29,8 @@ LevelSessionSchema = c.object _.extend LevelSessionSchema.properties, # denormalization + browser: + type: 'object' creatorName: type: 'string' levelName: @@ -83,10 +85,10 @@ _.extend LevelSessionSchema.properties, 'string' ] playing: - type: 'boolean' + type: 'boolean' # Not tracked any more frame: - type: 'number' - thangs: + type: 'number' # Not tracked any more + thangs: # ... what is this? Is this used? type: 'object' additionalProperties: title: 'Thang' @@ -114,6 +116,12 @@ _.extend LevelSessionSchema.properties, description: 'How many times the session has been submitted for real-time playback (can affect the random seed).' type: 'integer' minimum: 0 + difficulty: + description: 'The highest difficulty level beaten, for use in increasing-difficulty replayable levels.' + type: 'integer' + minimum: 0 + lastUnsuccessfulSubmissionTime: c.date + description: 'The last time that real-time submission was started without resulting in a win.' flagHistory: description: 'The history of flag events during the last real-time playback submission.' type: 'array' @@ -127,6 +135,12 @@ _.extend LevelSessionSchema.properties, x: {type: 'number'} y: {type: 'number'} source: {type: 'string', enum: ['click']} # Do not store 'code' flag events in the session. + topScores: c.array {}, + c.object {}, + type: c.shortString('enum': ['time', 'damage-taken', 'damage-dealt', 'gold-collected', 'difficulty']) + date: c.date + description: 'When the submission achieving this score happened.' + score: {type: 'number'} # Store 'time' and 'damage-taken' as negative numbers so the index works. code: type: 'object' @@ -194,6 +208,12 @@ _.extend LevelSessionSchema.properties, type: 'boolean' description: 'Whether this session is still in the first ranking chain after being submitted.' + randomSimulationIndex: + type: 'number' + description: 'A random updated every time the game is randomly simulated for a uniform random distribution of simulations (see #2448).' + minimum: 0 + maximum: 1 + unsubscribed: type: 'boolean' description: 'Whether the player has opted out of receiving email updates about ladder rankings for this session.' @@ -227,7 +247,7 @@ _.extend LevelSessionSchema.properties, description: 'The date a match was computed.' playtime: title: 'Playtime so far' - description: 'The total seconds of playtime on this session when the match was computed.' + description: 'The total seconds of playtime on this session when the match was computed. Not currently tracked.' type: 'number' metrics: type: 'object' @@ -271,6 +291,8 @@ _.extend LevelSessionSchema.properties, codeLanguage: type: ['string', 'null'] # 'null' in case an opponent session got corrupted, don't care much here description: 'What submittedCodeLanguage the opponent used during the match' + simulator: {type: 'object', description: 'Holds info on who simulated the match, and with what tools.'} + randomSeed: {description: 'Stores the random seed that was used during this match.'} c.extendBasicProperties LevelSessionSchema, 'level.session' c.extendPermissionsProperties LevelSessionSchema, 'level.session' diff --git a/app/schemas/models/patch.coffee b/app/schemas/models/patch.coffee index 918644c8b..0284f8164 100644 --- a/app/schemas/models/patch.coffee +++ b/app/schemas/models/patch.coffee @@ -1,6 +1,6 @@ c = require './../schemas' -patchables = ['level', 'thang_type', 'level_system', 'level_component', 'article'] +patchables = ['level', 'thang_type', 'level_system', 'level_component', 'article', 'achievement', 'campaign', 'poll'] PatchSchema = c.object({title: 'Patch', required: ['target', 'delta', 'commitMessage']}, { delta: {title: 'Delta', type: ['array', 'object']} diff --git a/app/schemas/models/payment.schema.coffee b/app/schemas/models/payment.schema.coffee index 4d7ac2f63..cfb24eae2 100644 --- a/app/schemas/models/payment.schema.coffee +++ b/app/schemas/models/payment.schema.coffee @@ -3,23 +3,25 @@ c = require './../schemas' PaymentSchema = c.object({title: 'Payment', required: []}, { purchaser: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ]) # in case of gifts recipient: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ]) - - service: { enum: ['stripe', 'ios' ]} + + service: { enum: ['stripe', 'ios', 'external']} amount: { type: 'integer', description: 'Payment in cents.' } created: c.date({title: 'Created', readOnly: true}) gems: { type: 'integer', description: 'The number of gems acquired.' } - productID: { enum: ['gems_5', 'gems_10', 'gems_20']} - + productID: { enum: ['gems_5', 'gems_10', 'gems_20', 'custom']} + description: { type: 'string' } + ios: c.object({title: 'iOS IAP Data'}, { transactionID: { type: 'string' } rawReceipt: { type: 'string' } localPrice: { type: 'string' } }) - + stripe: c.object({title: 'Stripe Data'}, { timestamp: { type: 'integer', description: 'Unique identifier provided by the client, to guard against duplicate payments.' } chargeID: { type: 'string' } customerID: { type: 'string' } + invoiceID: { type: 'string' } }) }) diff --git a/app/schemas/models/poll.schema.coffee b/app/schemas/models/poll.schema.coffee new file mode 100644 index 000000000..a2860c3ec --- /dev/null +++ b/app/schemas/models/poll.schema.coffee @@ -0,0 +1,24 @@ +c = require './../schemas' + +PollSchema = c.object {title: 'Poll'} +c.extendNamedProperties PollSchema # name first + +_.extend PollSchema.properties, + description: {type: 'string', title: 'Description', description: 'Optional: extra context or explanation', format: 'markdown' } + answers: c.array {title: 'Answers'}, + c.object {required: ['key', 'text', 'i18n', 'votes']}, + key: c.shortString {title: 'Key', description: 'Key for recording votes, like 14-to-17', pattern: '^[a-z0-9-]+$'} + text: c.shortString {title: 'Text', description: 'Answer that the player will see, like 14 - 17.', format: 'markdown'} + i18n: {type: 'object', title: 'i18n', format: 'i18n', props: ['text']} + votes: {title: 'Votes', type: 'integer', minimum: 0} + i18n: {type: 'object', title: 'i18n', format: 'i18n', props: ['name', 'description']} + created: c.date {title: 'Created', readOnly: true} + priority: {title: 'Priority', description: 'Lower numbers will show earlier.', type: 'integer'} + userProperty: c.shortString {pattern: c.identifierPattern, description: 'Optional: store the answer inside the User object itself, also, with this property name.'} + +c.extendBasicProperties PollSchema, 'poll' +c.extendSearchableProperties PollSchema +c.extendTranslationCoverageProperties PollSchema +c.extendPatchableProperties PollSchema + +module.exports = PollSchema diff --git a/app/schemas/models/prepaid.schema.coffee b/app/schemas/models/prepaid.schema.coffee new file mode 100644 index 000000000..799ad409b --- /dev/null +++ b/app/schemas/models/prepaid.schema.coffee @@ -0,0 +1,14 @@ +c = require './../schemas' + +PrepaidSchema = c.object({title: 'Prepaid', required: ['creator', 'redeemer', 'type']}, { + creator: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ]) + redeemer: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ]) + code: c.shortString(title: "Unique code to redeem") + type: { type: 'string' } + status: { enum: ['active', 'used'], default: 'active' } + properties: {type: 'object'} +}) + +c.extendBasicProperties(PrepaidSchema, 'prepaid') + +module.exports = PrepaidSchema diff --git a/app/schemas/models/thang_type.coffee b/app/schemas/models/thang_type.coffee index 3fdabbbb9..5d4d9624e 100644 --- a/app/schemas/models/thang_type.coffee +++ b/app/schemas/models/thang_type.coffee @@ -137,6 +137,7 @@ _.extend ThangTypeSchema.properties, head: { type: 'string', format: 'image-file', title: 'Head' } hair: { type: 'string', format: 'image-file', title: 'Hair' } thumb: { type: 'string', format: 'image-file', title: 'Thumb' } + wizardHand: { type: 'string', format: 'image-file', title: 'Wizard Hand' } dollImages: c.object { title: 'Paper Doll Images' }, male: { type: 'string', format: 'image-file', title: ' Male' } female: { type: 'string', format: 'image-file', title: ' Female' } @@ -145,7 +146,9 @@ _.extend ThangTypeSchema.properties, maleRanger: { type: 'string', format: 'image-file', title: 'Glove (Male Ranger)' } maleRangerThumb: { type: 'string', format: 'image-file', title: 'Thumb (Male Ranger)' } femaleRanger: { type: 'string', format: 'image-file', title: 'Glove (Female Ranger)' } - femaleRangeThumbr: { type: 'string', format: 'image-file', title: 'Thumb (Female Ranger)' } + femaleRangerThumb: { type: 'string', format: 'image-file', title: 'Thumb (Female Ranger)' } + maleBack: { type: 'string', format: 'image-file', title: ' Male Back' } + femaleBack: { type: 'string', format: 'image-file', title: ' Female Back' } colorGroups: c.object title: 'Color Groups' additionalProperties: @@ -165,6 +168,8 @@ _.extend ThangTypeSchema.properties, i18n: {type: 'object', format: 'i18n', props: ['name', 'description', 'extendedName', 'unlockLevelName'], description: 'Help translate this ThangType\'s name and description.'} extendedName: {type: 'string', title: 'Extended Hero Name', description: 'The long form of the hero\'s name. Ex.: "Captain Anya Weston".'} unlockLevelName: {type: 'string', title: 'Unlock Level Name', description: 'The name of the level in which the hero is unlocked.'} + tasks: c.array {title: 'Tasks', description: 'Tasks to be completed for this ThangType.'}, c.task + ThangTypeSchema.required = [] diff --git a/app/schemas/models/trial_request.schema.coffee b/app/schemas/models/trial_request.schema.coffee new file mode 100644 index 000000000..68e4a3a48 --- /dev/null +++ b/app/schemas/models/trial_request.schema.coffee @@ -0,0 +1,18 @@ +c = require './../schemas' + +TrialRequestSchema = c.object { + title: 'Trial request', + required: ['type'] +} + +_.extend TrialRequestSchema.properties, + applicant: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}]) + prepaidCode: c.objectId() + reviewDate: c.date({readOnly: true}) + reviewer: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}]) + properties: {type: 'object', description: 'Data specific to this request.'} + status: {type: 'string', 'enum': ['submitted', 'approved', 'denied']} + type: {type: 'string', 'enum': ['subscription']} + +c.extendBasicProperties TrialRequestSchema, 'TrialRequest' +module.exports = TrialRequestSchema diff --git a/app/schemas/models/user-polls-record.schema.coffee b/app/schemas/models/user-polls-record.schema.coffee new file mode 100644 index 000000000..132d9383e --- /dev/null +++ b/app/schemas/models/user-polls-record.schema.coffee @@ -0,0 +1,20 @@ +c = require './../schemas' + +UserPollsRecordSchema = c.object {title: 'UserPollsRecord'} + +_.extend UserPollsRecordSchema.properties, + user: c.stringID {links: [{rel: 'extra', href: '/db/user/{($)}'}]} + polls: # Poll ID strings -> answer key strings + type: 'object' + additionalProperties: c.shortString {pattern: '^[a-z0-9-]+$'} + rewards: # Poll ID strings -> reward objects, for calculating gems + type: 'object' + additionalProperties: c.object {}, + random: {type: 'number', minimum: 0, maximum: 1} + level: {type: 'integer', minimum: 1} + level: {type: 'integer', minimum: 1, description: 'The player level when last saved.'} + changed: c.date title: 'Changed', readOnly: true # Controls when next poll is available + +c.extendBasicProperties UserPollsRecordSchema, 'user-polls-record' + +module.exports = UserPollsRecordSchema diff --git a/app/schemas/models/user.coffee b/app/schemas/models/user.coffee index 1067744e9..2a422fab4 100644 --- a/app/schemas/models/user.coffee +++ b/app/schemas/models/user.coffee @@ -53,7 +53,8 @@ _.extend UserSchema.properties, iosIdentifierForVendor: c.shortString({format: 'hidden'}) firstName: c.shortString({title: 'First Name'}) lastName: c.shortString({title: 'Last Name'}) - gender: {type: 'string', 'enum': ['male', 'female']} + gender: {type: 'string', 'enum': ['male', 'female', 'secret', 'trans']} + ageRange: {type: 'string'} # 'enum': ['0-13', '14-17', '18-24', '25-34', '35-44', '45-100'] password: {type: 'string', maxLength: 256, minLength: 2, title: 'Password'} passwordReset: {type: 'string'} photoURL: {type: 'string', format: 'image-file', title: 'Profile Picture', description: 'Upload a 256x256px or larger image to serve as your profile picture.'} @@ -62,10 +63,10 @@ _.extend UserSchema.properties, githubID: {type: 'integer', title: 'GitHub ID'} gplusID: c.shortString({title: 'G+ ID'}) - wizardColor1: c.pct({title: 'Wizard Clothes Color'}) + wizardColor1: c.pct({title: 'Wizard Clothes Color'}) # No longer used volume: c.pct({title: 'Volume'}) music: { type: 'boolean' } - autocastDelay: { type: 'integer' } + autocastDelay: { type: 'integer' } # No longer used lastLevel: { type: 'string' } heroConfig: c.HeroConfigSchema @@ -85,6 +86,12 @@ _.extend UserSchema.properties, recruitNotes: {$ref: '#/definitions/emailSubscription'} employerNotes: {$ref: '#/definitions/emailSubscription'} + oneTimes: c.array {title: 'One-time emails'}, + c.object {title: 'One-time email', required: ['type', 'email']}, + type: c.shortString() # E.g 'subscribe modal parent' + email: c.shortString() + sent: c.date() # Set when sent + # server controlled permissions: c.array {}, c.shortString() dateCreated: c.date({title: 'Date Joined'}) @@ -103,7 +110,7 @@ _.extend UserSchema.properties, emailHash: {type: 'string'} #Internationalization stuff - preferredLanguage: {type: 'string', 'enum': c.getLanguageCodeArray()} + preferredLanguage: {'enum': [null].concat(c.getLanguageCodeArray())} signedCLA: c.date({title: 'Date Signed the CLA'}) wizard: c.object {}, @@ -267,11 +274,46 @@ _.extend UserSchema.properties, levelSystemMiscPatches: c.int() thangTypeTranslationPatches: c.int() thangTypeMiscPatches: c.int() + achievementTranslationPatches: c.int() + achievementMiscPatches: c.int() + pollTranslationPatches: c.int() + pollMiscPatches: c.int() + campaignTranslationPatches: c.int() + campaignMiscPatches: c.int() earned: c.RewardSchema 'earned by achievements' purchased: c.RewardSchema 'purchased with gems or money' + deleted: {type: 'boolean'} + dateDeleted: c.date() spent: {type: 'number'} - stripeCustomerID: { type: 'string' } + stripeCustomerID: { type: 'string' } # TODO: Migrate away from this property + + stripe: c.object {}, { + customerID: { type: 'string' } + planID: { enum: ['basic'], description: 'Determines if a user has or wants to subscribe' } + subscriptionID: { type: 'string', description: 'Determines if a user is subscribed' } + token: { type: 'string' } + couponID: { type: 'string' } + free: { type: ['boolean', 'string'], format: 'date-time', description: 'Type string is subscription end date' } + prepaidCode: c.shortString description: 'Prepaid code to apply to sub purchase' + + # Sponsored subscriptions + subscribeEmails: c.array { description: 'Input for subscribing other users' }, c.shortString() + unsubscribeEmail: { type: 'string', description: 'Input for unsubscribing a sponsored user' } + recipients: c.array { title: 'Recipient subscriptions owned by this user' }, + c.object { required: ['userID', 'subscriptionID'] }, + userID: c.objectId { description: 'User ID of recipient' } + subscriptionID: { type: 'string' } + couponID: { type: 'string' } + sponsorID: c.objectId { description: "User ID that owns this user's subscription" } + sponsorSubscriptionID: { type: 'string', description: 'Sponsor aggregate subscription used to pay for all recipient subs' } + } + + siteref: { type: 'string' } + referrer: { type: 'string' } + chinaVersion: { type: 'boolean' } + + clans: c.array {}, c.objectId() c.extendBasicProperties UserSchema, 'user' diff --git a/app/schemas/schemas.coffee b/app/schemas/schemas.coffee index d9c62f40e..c02c07cdd 100644 --- a/app/schemas/schemas.coffee +++ b/app/schemas/schemas.coffee @@ -15,10 +15,13 @@ me.object = (ext, props) -> combine({type: 'object', additionalProperties: false me.array = (ext, items) -> combine({type: 'array', items: items or {}}, ext) me.shortString = (ext) -> combine({type: 'string', maxLength: 100}, ext) me.pct = (ext) -> combine({type: 'number', maximum: 1.0, minimum: 0.0}, ext) -me.date = (ext) -> combine({type: ['object', 'string'], format: 'date-time'}, ext) -# should just be string (Mongo ID), but sometimes mongoose turns them into objects representing those, so we are lenient -me.objectId = (ext) -> schema = combine({type: ['object', 'string']}, ext) -me.stringID = (ext) -> schema = combine({type: 'string', minLength: 24, maxLength: 24}, ext) + +# Dates should usually be strings, ObjectIds should be strings: https://github.com/codecombat/codecombat/issues/1384 +me.date = (ext) -> combine({type: ['object', 'string'], format: 'date-time'}, ext) # old +me.stringDate = (ext) -> combine({type: ['string'], format: 'date-time'}, ext) # new +me.objectId = (ext) -> schema = combine({type: ['object', 'string']}, ext) # old +me.stringID = (ext) -> schema = combine({type: 'string', minLength: 24, maxLength: 24}, ext) # use for anything new + me.url = (ext) -> combine({type: 'string', format: 'url', pattern: urlPattern}, ext) me.int = (ext) -> combine {type: 'integer'}, ext me.float = (ext) -> combine {type: 'number'}, ext @@ -143,7 +146,7 @@ me.getLanguageCodeArray = -> return Language.languageCodes me.getLanguagesObject = -> return Language - + me.extendTranslationCoverageProperties = (schema) -> schema.properties = {} unless schema.properties? schema.properties.i18nCoverage = { title: 'i18n Coverage', type: 'array', items: { type: 'string' }} @@ -207,7 +210,7 @@ me.activity = me.object {description: 'Stats on an activity'}, last: me.date() count: {type: 'integer', minimum: 0} -me.terrainString = me.shortString {enum: ['Grass', 'Dungeon', 'Indoor'], title: 'Terrain', description: 'Which terrain type this is.'} +me.terrainString = me.shortString {enum: ['Grass', 'Dungeon', 'Indoor', 'Desert', 'Mountain', 'Glacier', 'Volcano'], title: 'Terrain', description: 'Which terrain type this is.'} me.HeroConfigSchema = me.object {description: 'Which hero the player is using, equipped with what inventory.'}, inventory: @@ -227,4 +230,31 @@ me.RewardSchema = (descriptionFragment='earned by achievements') -> me.stringID(links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], title: 'Item ThangType', description: 'A reference to the earned item ThangType.', format: 'thang-type') levels: me.array {uniqueItems: true, description: "Levels #{descriptionFragment}."}, me.stringID(links: [{rel: 'db', href: '/db/level/{($)}/version'}], title: 'Level', description: 'A reference to the earned Level.', format: 'latest-version-original-reference') - gems: me.int {description: "Gems #{descriptionFragment}."} + gems: me.float {description: "Gems #{descriptionFragment}."} + +me.task = me.object {title: 'Task', description: 'A task to be completed', format: 'task', default: {name: 'TODO', complete: false}}, + name: {title: 'Name', description: 'What must be done?', type: 'string'} + complete: {title: 'Complete', description: 'Whether this task is done.', type: 'boolean', format: 'checkbox'} + +me.concept = me.shortString enum: [ + 'advanced_strings' + 'algorithms' + 'arguments' + 'arithmetic' + 'arrays' + 'basic_syntax' + 'boolean_logic' + 'break_statements' + 'classes' + 'for_loops' + 'functions' + 'if_statements' + 'input_handling' + 'math_operations' + 'object_literals' + 'strings' + 'variables' + 'vectors' + 'while_loops' + 'recursion' + ] diff --git a/app/schemas/subscriptions/editor.coffee b/app/schemas/subscriptions/editor.coffee index 2665438be..a0a416f25 100644 --- a/app/schemas/subscriptions/editor.coffee +++ b/app/schemas/subscriptions/editor.coffee @@ -1,6 +1,9 @@ c = require 'schemas/schemas' module.exports = + 'editor:campaign-analytics-modal-closed': c.object {title: 'Campaign editor analytics modal closed'}, + targetLevelSlug: {type: 'string'} + 'editor:save-new-version': c.object {title: 'Save New Version', description: 'Published when a version gets saved', required: ['major', 'commitMessage']}, major: {type: 'boolean'} commitMessage: {type: 'string'} diff --git a/app/schemas/subscriptions/god.coffee b/app/schemas/subscriptions/god.coffee index d7701999a..6fb79fca1 100644 --- a/app/schemas/subscriptions/god.coffee +++ b/app/schemas/subscriptions/god.coffee @@ -35,6 +35,7 @@ module.exports = 'god:infinite-loop': c.object {required: ['firstWorld']}, firstWorld: {type: 'boolean'} + nonUserCodeProblem: {type: 'boolean'} 'god:new-world-created': worldUpdatedEventSchema diff --git a/app/schemas/subscriptions/ipad.coffee b/app/schemas/subscriptions/ipad.coffee index 9c16a600c..2700d2188 100644 --- a/app/schemas/subscriptions/ipad.coffee +++ b/app/schemas/subscriptions/ipad.coffee @@ -6,7 +6,10 @@ module.exports = c.object {}, price: { type: 'string' } id: { type: 'string' } - + + 'ipad:language-chosen': c.object {}, + language: { type: 'string' } + 'ipad:iap-complete': c.object {}, productID: { type: 'string' } diff --git a/app/schemas/subscriptions/misc.coffee b/app/schemas/subscriptions/misc.coffee index b3affb40d..3a43ab4ab 100644 --- a/app/schemas/subscriptions/misc.coffee +++ b/app/schemas/subscriptions/misc.coffee @@ -35,7 +35,7 @@ module.exports = 'router:navigate': c.object {required: ['route']}, route: {type: 'string'} view: {type: 'object'} - viewClass: {type: 'function'} + viewClass: {type: ['function', 'string']} viewArgs: {type: 'array'} 'router:navigated': c.object {required: ['route']}, @@ -56,6 +56,8 @@ module.exports = 'buy-gems-modal:purchase-initiated': c.object {required: ['productID']}, productID: { type: 'string' } + 'subscribe-modal:subscribed': c.object {} + 'stripe:received-token': c.object { required: ['token'] }, token: { type: 'object', properties: { id: {type: 'string'} diff --git a/app/schemas/subscriptions/play.coffee b/app/schemas/subscriptions/play.coffee index 1555942a7..d4f967578 100644 --- a/app/schemas/subscriptions/play.coffee +++ b/app/schemas/subscriptions/play.coffee @@ -105,8 +105,6 @@ module.exports = 'playback:ended-changed': c.object {required: ['ended']}, ended: {type: 'boolean'} - 'level:play-next-level': c.object {} - 'level:toggle-playing': c.object {} 'level:toggle-grid': c.object {} @@ -167,9 +165,9 @@ module.exports = ] timedOut: {type: 'boolean'} - 'level:edit-wizard-settings': c.object {} - 'level:hero-config-changed': c.object {} 'level:hero-selection-updated': c.object {required: ['hero']}, hero: {type: 'object'} + + 'level:subscription-required': c.object {} diff --git a/app/schemas/subscriptions/tome.coffee b/app/schemas/subscriptions/tome.coffee index 7cdcef7a0..6dd258cf4 100644 --- a/app/schemas/subscriptions/tome.coffee +++ b/app/schemas/subscriptions/tome.coffee @@ -7,16 +7,20 @@ module.exports = preload: {type: 'boolean'} realTime: {type: 'boolean'} - 'tome:cast-spells': c.object {title: 'Cast Spells', description: 'Published when spells are cast', required: ['spells', 'preload', 'realTime', 'submissionCount', 'flagHistory']}, + 'tome:cast-spells': c.object {title: 'Cast Spells', description: 'Published when spells are cast', required: ['spells', 'preload', 'realTime', 'submissionCount', 'flagHistory', 'difficulty']}, spells: [type: 'object'] preload: [type: 'boolean'] realTime: [type: 'boolean'] submissionCount: [type: 'integer'] flagHistory: [type: 'array'] + difficulty: [type: 'integer'] 'tome:manual-cast': c.object {title: 'Manually Cast Spells', description: 'Published when you wish to manually recast all spells', required: []}, realTime: {type: 'boolean'} + 'tome:manual-cast-denied': c.object {title: 'Manual Cast Denied', description: 'Published when player attempts to submit for real-time playback, but must wait after a replayable level failure.', required: ['timeUntilResubmit']}, + timeUntilResubmit: {type: 'number'} + 'tome:spell-created': c.object {title: 'Spell Created', description: 'Published after a new spell has been created', required: ['spell']}, spell: {type: 'object'} diff --git a/app/styles/account/account-settings-view.sass b/app/styles/account/account-settings-view.sass index 66e8ac787..ae8cd31de 100644 --- a/app/styles/account/account-settings-view.sass +++ b/app/styles/account/account-settings-view.sass @@ -1,7 +1,6 @@ #account-settings-root-view //- Fixed save button - #site-content-area padding-bottom: 44px @@ -19,29 +18,30 @@ &.btn-info, &.btn-danger opacity: 1.0 - + #account-settings-view - + .row padding-top: 20px - + //- Panels - .panel-heading font-family: Open Sans Condensed font-weight: bold - + .panel-title font-size: 20px - - //- Panel specific stuff + #delete-account-panel-title + color: #F00 + + //- Panel specific stuff .profile-photo max-width: 100% max-height: 200px display: block margin-bottom: 10px - + #email-panel #specific-notification-settings padding-left: 20px diff --git a/app/styles/account/invoices-view.sass b/app/styles/account/invoices-view.sass new file mode 100644 index 000000000..392b737f9 --- /dev/null +++ b/app/styles/account/invoices-view.sass @@ -0,0 +1,9 @@ +#invoices-view + .form + #amount + width: 100px + #description + min-width: 400px + width: auto + #pay-button + width: auto diff --git a/app/styles/account/profile.sass b/app/styles/account/profile-view.sass similarity index 100% rename from app/styles/account/profile.sass rename to app/styles/account/profile-view.sass diff --git a/app/styles/account/subscription-view.sass b/app/styles/account/subscription-view.sass new file mode 100644 index 000000000..8dbfe70b8 --- /dev/null +++ b/app/styles/account/subscription-view.sass @@ -0,0 +1,40 @@ +#subscription-view + .start-subscription-button, .end-subscription-button + margin-bottom: 20px + float: left + width: 100% + + .unsubscribe-feedback + width: 100% + + h3 + margin-top: 0 + + textarea + width: 100% + + button.btn + width: 100% + margin-top: 12px + + // Sponsored subscriptions + + .recipient-emails + min-width: 50% + + .recipients-subscribe-button + margin-top: 10px + + .recipient-unsubscribe-button + width: auto + + .confirm-recipient-unsubscribe-button + width: auto + + .discount-table + width: 50% + + .recipients-table + width: 50% + .recipient-unsubscribe + text-align: right diff --git a/app/styles/account/unsubscribe.sass b/app/styles/account/unsubscribe-view.sass similarity index 100% rename from app/styles/account/unsubscribe.sass rename to app/styles/account/unsubscribe-view.sass diff --git a/app/styles/account/wizard-settings.sass b/app/styles/account/wizard-settings.sass deleted file mode 100644 index 9a93d6613..000000000 --- a/app/styles/account/wizard-settings.sass +++ /dev/null @@ -1,31 +0,0 @@ -#wizard-settings-view - h3 - text-align: center - - #tinting-display - float: right - - width: 450px - margin: 0 auto - - label - cursor: pointer - - #color-settings table - float: left - width: 250px - cursor: pointer - - .minicolors-input - display: none - - .minicolors-swatch - position: static - width: 40px - cursor: pointer - - .enabled-cell - width: 30px - - .color-cell - width: 50px \ No newline at end of file diff --git a/app/styles/achievements.sass b/app/styles/achievements.sass index 396472b6a..2dec35b71 100644 --- a/app/styles/achievements.sass +++ b/app/styles/achievements.sass @@ -56,7 +56,7 @@ $user-achievements-scale: 0.8 font-size: 18px > .achievement-description - white-space: initial + white-space: normal font-size: 12px line-height: 1.3em max-height: 2.6em @@ -95,7 +95,7 @@ $user-achievements-scale: 0.8 padding: $overall-scale * 24px $overall-scale * 30px $overall-scale * 20px $overall-scale * 60px .achievement-title - font-family: Open Sans Condensed + font-family: $headings-font-family font-variant: small-caps font-size: $overall-scale * 28px padding-left: $overall-scale * -50px diff --git a/app/styles/admin.sass b/app/styles/admin.sass index 91219b7a3..e111dd9d4 100644 --- a/app/styles/admin.sass +++ b/app/styles/admin.sass @@ -1,2 +1,5 @@ #admin-view - color: black \ No newline at end of file + color: black + + #free-sub-input + min-width: 50% diff --git a/app/styles/admin/analytics-subscriptions.sass b/app/styles/admin/analytics-subscriptions.sass new file mode 100644 index 000000000..6d7f732d1 --- /dev/null +++ b/app/styles/admin/analytics-subscriptions.sass @@ -0,0 +1,63 @@ +#admin-analytics-subscriptions-view + + #site-content-area + width: 100% + + .big-stat + width: auto + + .total-count + color: green + .remaining-count + color: blue + .cancelled-count + color: red + .churn-count + color: orange + .growth-rate + color: green + + .count + font-size: 50pt + .description + font-size: 8pt + + .line-graph-label + font-size: 10pt + font-weight: normal + .line-graph-container + height: 500px + width: 100% + + .x.axis + font-size: 9pt + path + display: none + .y.axis + font-size: 9pt + path + display: none + .key-line + font-size: 9pt + .key-text + font-size: 9pt + .graph-point-info-container + display: none + position: absolute + padding: 10px + border: 1px solid black + z-index: 3 + background-color: blanchedalmond + font-size: 10pt + + .subscribers-thead + font-size: 10pt + th + padding: 2px + + .subscribers-tbody + font-size: 9pt + td + padding: 2px + max-width: 160px + overflow: hidden diff --git a/app/styles/admin/trial-requests.sass b/app/styles/admin/trial-requests.sass new file mode 100644 index 000000000..93f80862e --- /dev/null +++ b/app/styles/admin/trial-requests.sass @@ -0,0 +1,16 @@ +#admin-trial-requests-view + + #site-content-area + width: 100% + + .btn-deny + float: right + + .status-cell + width: 120px + + td.created + min-width: 90px + + td.reviewed + min-width: 90px diff --git a/app/styles/bootstrap/_variables.scss b/app/styles/bootstrap/_variables.scss index e2b5af123..06930f639 100644 --- a/app/styles/bootstrap/_variables.scss +++ b/app/styles/bootstrap/_variables.scss @@ -75,7 +75,7 @@ $font-size-h6: ceil($font-size-base * 0.85) !default; // ~12px $line-height-base: 1.428571429 !default; // 20/14 $line-height-computed: floor($font-size-base * $line-height-base) !default; // ~20px -$headings-font-family: 'Open Sans Condensed', cursive; // empty to use BS default, $baseFontFamily; +$headings-font-family: 'Open Sans Condensed', "Helvetica Neue", Helvetica, Arial, sans-serif; // empty to use BS default, $baseFontFamily; $headings-font-weight: 700 !default; $headings-line-height: 1.1 !default; $headings-color: #317EAC; diff --git a/app/styles/clans/clan-details.sass b/app/styles/clans/clan-details.sass new file mode 100644 index 000000000..770cf3641 --- /dev/null +++ b/app/styles/clans/clan-details.sass @@ -0,0 +1,187 @@ +#clan-details-view + + th + font-size: 16px + + .join-clan-link + width: 390px + + .join-link-prompt + font-weight: bold + + .stats-table + width: 400px + background: rgba(0, 0, 0, 0.0) + + #editDescriptionModal .modal-dialog + background-color: white + + #editNameModal .modal-dialog + background-color: white + max-width: 400px + + .edit-description-input + width: 100% + + .edit-name-input + width: 100% + + $spriteSheetSize: 30px + + .remove-hero-cell + width: 100px + + .hero-icon-cell + display: inline-block + width: 30px + height: 50px + margin: 0px 2px + vertical-align: middle + + td.hero-icon-cell + display: table-cell + + .level-cell + width: 50px + text-align: center + vertical-align: middle + + .name-cell + width: 100px + vertical-align: middle + + .achievements-cell + text-align: center + vertical-align: middle + + .latest-achievement-cell + vertical-align: middle + + + .member-header + cursor: pointer + + .progress-header + cursor: pointer + + .progress-key + cursor: default + display: inline-block + white-space: nowrap + font-size: 9pt + font-weight: normal + border: 1px solid gray + border-radius: 5px + margin: 0px + padding: 2px + + .progress-key-started + background-color: lightgreen + + .progress-key-complete + background-color: lightgray + margin-left: 14px + + .expand-progress-checkbox + margin-left: 14px + + .expand-progress-label + font-weight: normal + font-size: 14px + + .progress-cell + padding: 2px + padding-bottom: 10px + + .level-popup-container + display: none + position: absolute + padding: 10px + border: 1px solid black + z-index: 3 + background-color: blanchedalmond + font-size: 10pt + + .level-progression-concepts + color: #317EAC + font-size: 12pt + font-weight: bold + margin-top: 8px + margin-bottom: 4px + + .level-progression-levels + color: #317EAC + font-size: 12pt + font-weight: bold + margin-top: 8px + + .level-progression-campaign + font-size: 10pt + font-weight: bold + margin-bottom: 4px + margin-top: 4px + + .progress-level-cell + display: inline-block + white-space: nowrap + font-size: 9pt + border: 1px solid gray + border-radius: 5px + margin: 0px + padding: 2px + + .progress-level-cell-started + cursor: pointer + background-color: lightgreen + + .progress-level-cell-complete + cursor: pointer + background-color: lightgray + + .player-hero-icon + background: transparent url(/images/pages/play/play-spritesheet.png) + background-size: cover + background-position: (-2 * $spriteSheetSize) 0 + display: inline-block + width: 30px + height: 30px + margin: 0px 2px + vertical-align: middle + + .player-hero-icon + background-position: (-4 * $spriteSheetSize) 0 + + &.knight + background-position: (-5 * $spriteSheetSize) 0 + &.librarian + background-position: (-6 * $spriteSheetSize) 0 + &.ninja + background-position: (-7 * $spriteSheetSize) 0 + &.potion-master + background-position: (-8 * $spriteSheetSize) 0 + &.samurai + background-position: (-9 * $spriteSheetSize) 0 + &.trapper + background-position: (-10 * $spriteSheetSize) 0 + &.forest-archer + background-position: (-11 * $spriteSheetSize) 0 + &.sorcerer + background-position: (-12 * $spriteSheetSize) 0 + + td.code-language-cell + width: 30px + vertical-align: middle + + .code-language-cell + vertical-align: middle + + span.code-language-cell + background: transparent url(/images/common/code_languages/javascript_small.png) + background-size: cover + display: inline-block + width: 30px + height: 30px + margin: 0px 2px + + .remove-member-cell + vertical-align: middle diff --git a/app/styles/clans/clans.sass b/app/styles/clans/clans.sass new file mode 100644 index 000000000..bbac33cea --- /dev/null +++ b/app/styles/clans/clans.sass @@ -0,0 +1,17 @@ +#clans-view + color: black + + .clan-title + cursor: pointer + + .create-clan-description + width: 50% + + .popover + max-width: 100% + + h3 + background: transparent + border: 0 + font-size: 30px + color: black diff --git a/app/styles/common/common.sass b/app/styles/common/common.sass index 240bceeec..77762321d 100644 --- a/app/styles/common/common.sass +++ b/app/styles/common/common.sass @@ -140,12 +140,12 @@ table.table body[lang='ja'] h1, h2, h3, h4, h5, h6 - font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", 'Open Sans Condensed', sans-serif + font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", 'Open Sans Condensed', 'Helvetica Neue', Helvetica, Arial, sans-serif font-variant: small-caps letter-spacing: -1px !important .header-font - font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", 'Open Sans Condensed', sans-serif + font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", 'Open Sans Condensed', 'Helvetica Neue', Helvetica, Arial, sans-serif font-variant: small-caps letter-spacing: -1px !important @@ -216,6 +216,10 @@ kbd width: 25px height: 25px + &.gem-30 + width: 30px + height: 30px + &.gem-40 width: 40px height: 40px @@ -225,8 +229,9 @@ kbd height: 60px .popover - border-image: url(/images/level/popover_background.png) 29 39 fill stretch - border-width: 15px 20px + border-style: solid + border-image: url(/images/level/popover_border_background.png) 16 12 fill stretch + border-width: 16px 12px .arrow display: none .btn @@ -238,26 +243,68 @@ kbd border: 0 border-radius: 0 @include box-shadow(none) + border-style: solid border-image: url(/images/common/button-background-active-border.png) 14 20 20 20 fill round border-width: 7px 10px 10px 10px padding: 0 - font-family: Open Sans Condensed + font-family: $headings-font-family text-transform: uppercase font-weight: bold color: rgb(248, 197, 146) + &.btn-lg + border-width: 14px 20px 20px 20px + height: 60px + line-height: 34px + + &.btn-primary + border-image-source: url(/images/common/button-background-primary-active-border.png) + &.btn-success + border-image-source: url(/images/common/button-background-success-active-border.png) + color: darken(white, 5%) + &.btn-warning + border-image-source: url(/images/common/button-background-warning-active-border.png) + color: darken(white, 5%) + &.btn-danger + border-image-source: url(/images/common/button-background-danger-active-border.png) + &:hover color: lighten(rgb(248, 197, 146), 5%) + &.btn-success + color: white + &.btn-warning + color: white &:active - border-image: url(/images/common/button-background-pressed-border.png) 14 16 16 20 fill round - padding: 2px 0 0 2px - border-width: 7px 8px 8px 10px + &.btn-lg + border-image-source: url(/images/common/button-background-pressed-border.png) + &:not(.btn-lg) + border-image: url(/images/common/button-background-pressed-border.png) 14 16 16 20 fill round + padding: 2px 0 0 2px + border-width: 7px 8px 8px 10px + + &.btn-primary + border-image-source: url(/images/common/button-background-primary-pressed-border.png) + &.btn-success + border-image-source: url(/images/common/button-background-success-pressed-border.png) + &.btn-warning + border-image-source: url(/images/common/button-background-warning-pressed-border.png) + &.btn-danger + border-image-source: url(/images/common/button-background-danger-pressed-border.png) &.disabled, &:disabled border-image: url(/images/common/button-background-disabled-border.png) 14 20 20 20 fill round @include opacity(1) + &.btn-primary + border-image-source: url(/images/common/button-background-primary-disabled-border.png) + &.btn-success + border-image-source: url(/images/common/button-background-success-inactive-border.png) + &.btn-warning + border-image-source: url(/images/common/button-background-warning-disabled-border.png) + &.btn-danger + border-image-source: url(/images/common/button-background-danger-disabled-border.png) + > * @include opacity(0.5) @@ -281,38 +328,65 @@ html.no-borderimage background-size: 100% 100% padding: 7px 10px 10px 10px + &.btn-primary + background-image: url(/images/common/button-background-primary-active.png) + &.btn-success + background-image: url(/images/common/button-background-success-active.png) + &.btn-warning + background-image: url(/images/common/button-background-warning-active.png) + &.btn-danger + background-image: url(/images/common/button-background-danger-active.png) + &:active background-image: url(/images/common/button-background-pressed.png) padding: 9px 8px 8px 12px border: 0 + &.btn-primary + background-image: url(/images/common/button-background-primary-pressed.png) + &.btn-success + background-image: url(/images/common/button-background-success-pressed.png) + &.btn-warning + background-image: url(/images/common/button-background-warning-pressed.png) + &.btn-danger + background-image: url(/images/common/button-background-danger-pressed.png) + &.disabled, &:disabled background-image: url(/images/common/button-background-disabled.png) + &.btn-primary + background-image: url(/images/common/button-background-primary-disabled.png) + &.btn-success + background-image: url(/images/common/button-background-success-inactive.png) + &.btn-warning + background-image: url(/images/common/button-background-warning-disabled.png) + &.btn-danger + background-image: url(/images/common/button-background-danger-disabled.png) + body > iframe[src^="https://apis.google.com"] display: none -#module-loading-list - .modal-content - background: white - border-shadow: 2px 2px 10px black +#module-load-progress + position: absolute + top: 0 + left: 0 + right: 0 + height: 5px + z-index: 1 + transition: 1s - ul - max-height: 500px - overflow: scroll - - li - padding: 2px 15px - font-size: 10px - .glyphicon - margin-right: 10px - - &.loading - .glyphicon-ok - display: none - - &.success - font-weight: bold - .glyphicon-minus - display: none \ No newline at end of file + .progress-bar + background-color: lightblue + +.treema-node input[type='checkbox'] + @include scale(1.25) + width: auto + margin: 8px 15px 8px 15px + +.particle-man + position: absolute + z-index: 100 + top: 0 + left: 0 + pointer-events: none diff --git a/app/styles/common/site-chrome.sass b/app/styles/common/site-chrome.sass index 24368c806..e8f8612ae 100644 --- a/app/styles/common/site-chrome.sass +++ b/app/styles/common/site-chrome.sass @@ -32,7 +32,11 @@ @media screen and ( max-height: 800px ) top: -80px - + + @media screen and ( max-width: 1024px ) + .multiplayer-nav-link + display: none + #nav-logo position: absolute margin-right: auto @@ -64,9 +68,9 @@ & > a, button, select font-size: 18px text-transform: uppercase - font-family: Open Sans Condensed + font-family: $headings-font-family margin: 0 7px - + button, select position: relative top: -3px @@ -83,6 +87,7 @@ .language-dropdown width: auto + padding: 0px 10px display: inline-block #site-nav-smooth-edge @@ -106,15 +111,20 @@ .user-dropdown-header background: #E4CF8C - height: 160px + height: auto padding: 10px text-align: center color: black border-bottom: #32281e 1px solid - img + .img-circle + background-position: center + background-size: cover border: #e3be7a 8px solid + width: 98px height: 98px // Includes the border + display: inline-block + vertical-align: middle &:hover box-shadow: 0 0 20px #e3be7a diff --git a/app/styles/contribute/contribute.sass b/app/styles/contribute/contribute.sass new file mode 100644 index 000000000..e662cdcd1 --- /dev/null +++ b/app/styles/contribute/contribute.sass @@ -0,0 +1,25 @@ +#contribute-view + .class_tile + position: relative + width: 330px + padding: 5px + float: left + + &:hover img + outline: 3px solid #161a9e + + .class_text + position: absolute + bottom: 5px + width: 300px + padding: 12px 12px 0 12px + z-index: 1 + background-color: rgba(255,255,255,.5) + + p + color: black + + h3 + color: black + padding-top: 0px + margin-top: 0px diff --git a/app/styles/contribute_classes.sass b/app/styles/contribute/contribute_classes.sass similarity index 82% rename from app/styles/contribute_classes.sass rename to app/styles/contribute/contribute_classes.sass index e4b8ff7fb..88fd6aa9a 100644 --- a/app/styles/contribute_classes.sass +++ b/app/styles/contribute/contribute_classes.sass @@ -2,6 +2,13 @@ #homepage_screenshot margin: 20px 0px + + .class_detail + float: left + + img + width: 360px + .signature text-align: right @@ -13,21 +20,9 @@ width: 150px margin: 10px 10px 20px 20px - #contribute-nav - float: left - max-width: 20% - width: 250px - box-sizing: border-box - margin-left: 20px - padding-top: 40px - - li - float: none - width: 100% - .class-main - margin-left: 25% - padding: 40px + margin-left: 33% + padding: 0px 40px 40px 40px box-sizing: border-box .header-scrolling-fix diff --git a/app/styles/courses/mock1/course-details.sass b/app/styles/courses/mock1/course-details.sass new file mode 100644 index 000000000..7d023e1a4 --- /dev/null +++ b/app/styles/courses/mock1/course-details.sass @@ -0,0 +1,141 @@ +#course-details-view + + .concept-completion-container + font-size: 10pt + + .summary-container + font-size: 14pt + + .statistics-container + font-size: 12pt + td + padding-right: 8px + + .table-concepts-summary + width: 100% + + .concept-summary + width: 100% + background-color: white + cursor: default + display: inline-block + white-space: nowrap + font-size: 9pt + font-weight: normal + border: 1px solid gray + border-radius: 5px + margin: 0px + padding: 2px + background-color: white + + #editSettingsModal .modal-dialog + background-color: white + font-size: 14pt + + .edit-description-input + width: 100% + + .edit-name-input + width: 50% + + .member-header + cursor: pointer + display: inline-block + padding: 2px + + .textarea-emails + width: 50% + + .select-language + width: 200px + display: inline + + .select-session + width: 300px + display: inline + + .progress-header + margin-right: 14px + cursor: pointer + + .progress-key + cursor: default + display: inline-block + white-space: nowrap + font-size: 9pt + font-weight: normal + border: 1px solid gray + border-radius: 5px + margin: 0px + padding: 2px + + .progress-key-started + background-color: lightgreen + + .progress-key-complete + background-color: lightgray + + .expand-progress-checkbox + margin-left: 14px + + .expand-progress-label + font-weight: normal + font-size: 14px + + .progress-cell + padding: 2px + padding-bottom: 10px + + .level-popup-container + display: none + position: absolute + padding: 10px + border: 1px solid black + z-index: 3 + background-color: blanchedalmond + font-size: 10pt + + .level-progression-concepts + color: #317EAC + font-size: 12pt + font-weight: bold + margin-top: 8px + margin-bottom: 4px + + .level-progression-levels + color: #317EAC + font-size: 12pt + font-weight: bold + margin-top: 8px + + .progress-level-cell + display: inline-block + white-space: nowrap + font-size: 9pt + border: 1px solid gray + border-radius: 5px + margin: 0px + padding: 2px + + .progress-level-cell-started + cursor: pointer + background-color: lightgreen + + .progress-level-cell-complete + cursor: pointer + background-color: lightgray + + .progress-concept-cell + display: inline-block + white-space: nowrap + font-size: 9pt + border: 1px solid gray + border-radius: 5px + margin: 0px + padding: 2px + + .progress-concept-cell-started + background-color: lightgreen + + .progress-concept-cell-complete + background-color: lightgray diff --git a/app/styles/courses/mock1/course-enroll.sass b/app/styles/courses/mock1/course-enroll.sass new file mode 100644 index 000000000..befeb2460 --- /dev/null +++ b/app/styles/courses/mock1/course-enroll.sass @@ -0,0 +1,14 @@ +#course-enroll-view + + .btn-buy + margin: 20px 0px + + .center + text-align: center + + .enroll-container + margin: 5% 20% + width: 60% + + .session-name + width: 300px diff --git a/app/styles/courses/mock1/course-info.sass b/app/styles/courses/mock1/course-info.sass new file mode 100644 index 000000000..b518f0de1 --- /dev/null +++ b/app/styles/courses/mock1/course-info.sass @@ -0,0 +1,31 @@ +#course-info-view + + .btn-enroll + margin-top: 20px + + .center + text-align: center + + .caption-text + font-size: 14px + + .concepts-container + width: 200px + + .contact-container + margin-top: 20px + text-align: center + + .info-container + margin: 0% 10% + font-size: 18px + + .monitoring-img-container + margin-top: 10px + + .praise-quote + font-size: 24px + font-style: italic + + .progress-container + font-size: 20px diff --git a/app/styles/courses/mock1/courses.sass b/app/styles/courses/mock1/courses.sass new file mode 100644 index 000000000..a652bff20 --- /dev/null +++ b/app/styles/courses/mock1/courses.sass @@ -0,0 +1,55 @@ +#courses-view + + .btn-continue + margin-top: 40px + + .center + text-align: center + + .code-input + width: 100% + .course-panel + margin: 20px + + .row-pick-class + display: none + + #continueModal .modal-dialog + background-color: white + max-width: 400px + + .instruction-label + font-size: 14pt + .or + margin-bottom: 20px + font-size: 14pt + + .btn-enroll + margin-top: 20px + + .center + text-align: center + + .caption-text + font-size: 14px + + .concepts-container + width: 200px + + .contact-container + margin-top: 20px + text-align: center + + .info-container + margin: 0% 10% + font-size: 18px + + .monitoring-img-container + margin-top: 10px + + .praise-quote + font-size: 24px + font-style: italic + + .progress-container + font-size: 20px diff --git a/app/styles/editor/campaign/campaign-analytics-modal.sass b/app/styles/editor/campaign/campaign-analytics-modal.sass new file mode 100644 index 000000000..b3596890e --- /dev/null +++ b/app/styles/editor/campaign/campaign-analytics-modal.sass @@ -0,0 +1,38 @@ +#campaign-analytics-modal + td + font-size: 9pt + max-width: 60px + td.completion-rate + max-width: 1000px + td.level + max-width: 1000px + .modal-dialog + width: 85% + .level-name-container + position: relative + max-width: 1000px + .level-name-background + position: absolute + height: 100% + left: 0px + top: 0px + background-color: green + opacity: 0.25 + .level-completion-container + position: relative + max-width: 1000px + .level-completion-background + position: absolute + height: 100% + width: 100% + left: 0px + top: 0px + .level-playtime-container + position: relative + .level-playtime-background + position: absolute + height: 100% + left: 0px + top: 0px + background-color: green + opacity: 0.25 diff --git a/app/styles/editor/campaign/campaign-editor-view.sass b/app/styles/editor/campaign/campaign-editor-view.sass new file mode 100644 index 000000000..87f60f7e3 --- /dev/null +++ b/app/styles/editor/campaign/campaign-editor-view.sass @@ -0,0 +1,29 @@ +#campaign-editor-view + #left-column + position: absolute + top: 0 + bottom: 0 + left: 0 + width: 25% + margin-right: 1200px + + .treema-root + max-height: 100% + overflow: scroll + + .completion + position: absolute + right: 0 + + #right-column + position: absolute + top: 0 + bottom: 0 + right: 0 + width: 75% + + .patches-view + position: absolute + left: 20px + top: 20px + z-index: 30 diff --git a/app/styles/editor/campaign/campaign-level-view.sass b/app/styles/editor/campaign/campaign-level-view.sass new file mode 100644 index 000000000..41da6ac8b --- /dev/null +++ b/app/styles/editor/campaign/campaign-level-view.sass @@ -0,0 +1,41 @@ +#campaign-level-view + background-color: white + position: absolute + top: 0 + left: 0 + right: 0 + z-index: 3 + + .tasks + padding: 15px + + .button.close + font-size: 63px + + .line-graph-label + font-size: 10pt + font-weight: normal + .line-graph-container + height: 500px + width: 100% + position: relative + .x.axis + font-size: 9pt + path + display: none + .y.axis + font-size: 9pt + path + display: none + .key-line + font-size: 9pt + .key-text + font-size: 9pt + .graph-point-info-container + display: none + position: absolute + padding: 10px + border: 1px solid black + z-index: 3 + background-color: blanchedalmond + font-size: 10pt diff --git a/app/styles/editor/component/thang-components-edit-view.sass b/app/styles/editor/component/thang-components-edit-view.sass index ffc9821b8..2ea931645 100644 --- a/app/styles/editor/component/thang-components-edit-view.sass +++ b/app/styles/editor/component/thang-components-edit-view.sass @@ -51,6 +51,7 @@ right: 0 left: 20px overflow: scroll + margin-bottom: 150px .selected-component .panel-heading - background-color: lightblue \ No newline at end of file + background-color: lightblue diff --git a/app/styles/editor/editor.sass b/app/styles/editor/editor.sass index 34eccdf79..1314f92c3 100644 --- a/app/styles/editor/editor.sass +++ b/app/styles/editor/editor.sass @@ -137,6 +137,8 @@ .treema-root background-color: white border-radius: 4px + &:focus + box-shadow: 0 0 10px blue .editor-nano-container position: static diff --git a/app/styles/editor/level/components_tab.sass b/app/styles/editor/level/components_tab.sass index 2e118a1e3..d10926859 100644 --- a/app/styles/editor/level/components_tab.sass +++ b/app/styles/editor/level/components_tab.sass @@ -33,6 +33,9 @@ #components-treema z-index: 11 + .not-present + opacity: 0.75 + .edit-component-container margin-left: 290px position: absolute diff --git a/app/styles/editor/level/thangs-tab-view.sass b/app/styles/editor/level/thangs-tab-view.sass index 84dd8b2c2..60fbded75 100644 --- a/app/styles/editor/level/thangs-tab-view.sass +++ b/app/styles/editor/level/thangs-tab-view.sass @@ -125,3 +125,8 @@ #contextmenu text-align: left + + #thang-components-edit-view + #thang-component-configs + // Get these away from the bottom of the screen so we can have dropdowns. + padding-bottom: 400px diff --git a/app/styles/editor/patches.sass b/app/styles/editor/patches.sass index f4130ec5a..d00a22c9c 100644 --- a/app/styles/editor/patches.sass +++ b/app/styles/editor/patches.sass @@ -2,5 +2,5 @@ .status-buttons margin-bottom: 10px - .patch-icon + .patch-row cursor: pointer diff --git a/app/styles/editor/poll/poll-edit-view.sass b/app/styles/editor/poll/poll-edit-view.sass new file mode 100644 index 000000000..c76880a62 --- /dev/null +++ b/app/styles/editor/poll/poll-edit-view.sass @@ -0,0 +1,17 @@ +#editor-poll-edit-view + .treema-root + margin: 28px 0px 20px + + .poll-tool-button + float: right + margin-top: 15px + margin-left: 10px + + textarea + width: 92% + height: 300px + + #poll-view + min-height: 200px + position: relative + z-index: 0 diff --git a/app/styles/front-view.sass b/app/styles/front-view.sass deleted file mode 100644 index b79e93a85..000000000 --- a/app/styles/front-view.sass +++ /dev/null @@ -1,30 +0,0 @@ -@import "app/styles/mixins" -@import "app/styles/bootstrap/variables" - -#front-view - h1 - text-align: center - margin-top: 0 - - .platform-choices - a - text-align: center - - .panel - @include transition(background-color 0.5s ease) - - &:hover - text-decoration: none - - .panel - background-color: rgb(230, 230, 255) - - .platform-ios - img - transform: scaleY(-1) - -@media only screen and (max-width: 800px) - #front-view - #site-slogan - font-size: 30px - margin-bottom: 30px diff --git a/app/styles/game-menu/guide-view.sass b/app/styles/game-menu/guide-view.sass deleted file mode 100644 index a25a38fa5..000000000 --- a/app/styles/game-menu/guide-view.sass +++ /dev/null @@ -1,27 +0,0 @@ -#guide-view, #settings-treema .treema-markdown - .nav-tabs - height: 41px - - .tab-content - padding-top: 20px - margin-bottom: 50px - - li:not(.active) a[data-toggle="tab"] - cursor: pointer - - img - display: block - margin: 0 auto - - img + em - display: block - margin: 0 auto - text-align: center - - hr - border-color: #5c5c5c - width: 80% - - table - width: 80% - margin: 20px 10% \ No newline at end of file diff --git a/app/styles/home.sass b/app/styles/home.sass index 0092035f6..f12074e9f 100644 --- a/app/styles/home.sass +++ b/app/styles/home.sass @@ -66,7 +66,7 @@ @media screen and ( max-height: 800px ) top: 451px - + .alert top: 213px border: 5px solid darkred @@ -82,3 +82,8 @@ a color: lighten(#0b63bc, 10%) + +body[lang='ru'], body[lang^='de'], body[lang^='pt-BR'], body[lang='pl'], body[lang='tr'], body[lang^='nl'], body[lang^='cs'], body[lang^='sv'], body[lang^='el'], body[lang^='hu'], body[lang^='bg'] + #home-view #slogan + font-size: 22px + line-height: 23px diff --git a/app/styles/i18n/i18n-edit-model-view.sass b/app/styles/i18n/i18n-edit-model-view.sass index 4438144b4..47575332e 100644 --- a/app/styles/i18n/i18n-edit-model-view.sass +++ b/app/styles/i18n/i18n-edit-model-view.sass @@ -7,6 +7,7 @@ .outer-content padding: 10px + overflow-y: scroll select - margin-bottom: 10px \ No newline at end of file + margin-bottom: 10px diff --git a/app/styles/legal.sass b/app/styles/legal.sass index 309a16bfb..ce9983b54 100644 --- a/app/styles/legal.sass +++ b/app/styles/legal.sass @@ -4,6 +4,3 @@ float: right width: 300px margin-left: 20px - - .cc-license-link - margin-left: 10px \ No newline at end of file diff --git a/app/styles/mixins.sass b/app/styles/mixins.sass index a010728da..833c5b564 100644 --- a/app/styles/mixins.sass +++ b/app/styles/mixins.sass @@ -72,7 +72,7 @@ -webkit-flex: $values -ms-flex: $values flex: $values - + @mixin order($val) -webkit-box-ordinal-group: $val -moz-box-ordinal-group: $val @@ -114,7 +114,7 @@ -ms-box-orient: vertical -webkit-flex-direction: column -ms-flex-direction: column - -flex-direction: column + flex-direction: column @mixin scaleX($ratio) -webkit-transform: scaleX($ratio) diff --git a/app/styles/modal/auth-modal.sass b/app/styles/modal/auth-modal.sass new file mode 100644 index 000000000..f87d478f3 --- /dev/null +++ b/app/styles/modal/auth-modal.sass @@ -0,0 +1,285 @@ +@import "app/styles/mixins" +@import "app/styles/bootstrap/variables" + +#auth-modal + + //- Clear modal defaults + + .modal-dialog + padding: 0 + width: 666px + height: 694px + + + //- Background + + .auth-modal-background + position: absolute + top: -90px + left: -40px + + + //- Header + + h1 + position: absolute + left: 183px + top: 0px + margin: 0 + width: 255px + text-align: center + color: rgb(254,188,68) + font-size: 32px + text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0 + + &.long-title + top: -14px + + + //- Close modal button + + #close-modal + position: absolute + left: 442px + top: -15px + width: 60px + height: 60px + color: white + text-align: center + font-size: 30px + padding-top: 15px + cursor: pointer + @include rotate(-3deg) + + &:hover + color: yellow + + + //- Modal body content + + .auth-form-content + position: absolute + top: 100px + left: 40px + width: 588px + + .help-block + margin: 0 + + .alert + margin-top: -25px + margin-bottom: 0 + padding: 10px 15px + + #recover-account-wrapper + float: right + margin-top: 7px + + .form-group + color: rgb(51,51,51) + padding: 0 + margin: 0 + + .input-border + border: 2px solid rgb(233, 221, 194) + border-radius: 4px + + input + background-color: rgb(239, 232, 216) + border: 2px solid rgb(26, 21, 18) + border-radius: 4px + + label + font-size: 20px + text-transform: uppercase + font-family: $headings-font-family + margin-bottom: 0 + + //- Check boxes + + .form-group.checkbox + margin: 10px 0 + + label + position: relative + line-height: 34px + + span:not(.custom-checkbox) + margin-left: 40px + + input + display: none + + & + .custom-checkbox + .glyphicon + display: none + + &:checked + .custom-checkbox .glyphicon + display: inline + color: rgb(248,169,67) + text-align: center + text-shadow: 0 0 3px black, 0 0 3px black, 0 0 3px black + font-size: 20px + position: relative + top: -2px + + .input-border + border-radius: 4px + height: 34px + width: 34px + position: absolute + + .custom-checkbox + border-radius: 4px + position: absolute + height: 30px + width: 30px + border: 2px solid rgb(26,21,18) + background: rgb(228,217,196) + text-align: center + + //- Primary auth button + + #login-button, #signup-button + position: absolute + top: 298px + height: 70px + font-size: 32px + line-height: 42px + border-style: solid + border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round + border-width: 14px 20px 20px 20px + color: white + + span + pointer-events: none + + &:hover + border-image: url(/images/level/code_toolbar_submit_button_zazz.png) 14 20 20 20 fill round + color: white + + &:active + border-image: url(/images/level/code_toolbar_submit_button_zazz_pressed.png) 14 20 20 20 fill round + padding: 2px 0 0 2px + color: white + + + //- Footer area + + .auth-network-logins + position: absolute + top: 470px + width: 580px + left: 48px + padding-left: 29px + + .btn.btn-lg.network-login + width: 251px + height: 60px + float: left + text-align: center + position: relative + margin-right: 15px + + .network-logo + height: 30px + position: absolute + left: -10px + top: 2px + + .sign-in-blurb + line-height: 34px + margin-left: 12px + + .fb-login-button + $scaleX: 251 / 64 + $scaleY: 60 / 23 + transform: scale($scaleX, $scaleY) + position: absolute + top: 4px + left: 74px + @include opacity(0.01) + + .gplus-login-wrapper + position: absolute + left: 65px + top: -6px + $scaleX: 251 / 84 + $scaleY: 60 / 24 + transform: scale($scaleX, $scaleY) + @include opacity(0.01) + + #github-login-button + position: relative + top: -1px + border-radius: 5px + img + width: 16px + margin: 0 5px 0 -5px + + #gplus-login-button + position: relative + top: 8px + + + //- Extra bottom pane area + .extra-pane + background-image: url(/images/pages/modal/auth/extra-pane.png) + width: 633px + height: 139px + padding: 23px 23px 23px 168px + position: absolute + top: 520px + + .switch-explanation + margin: 25px 10px 0 0 + width: 200px + color: rgb(254,188,68) + font-size: 20px + font-family: $headings-font-family + font-weight: bold + text-transform: uppercase + text-shadow: black 1px 1px 0, black -1px -1px 0, black 1px -1px 0, black -1px 1px 0, black 1px 0px 0, black 0px -1px 0, black -1px 0px 0, black 0px 1px 0 + float: left + + .btn + float: right + margin-top: 20px + width: 230px + height: 70px + line-height: 40px + + //- Login-specific styling + + &.login + .modal-dialog + height: 582px + + #login-button, #signup-button + top: 186px + + .auth-network-logins + top: 358px + + .extra-pane + top: 408px + + +html.no-borderimage #auth-modal + #login-button, #signup-button + border: 0 + background-image: url(/images/level/code_toolbar_submit_button_active.png) + background-size: 100% 100% + padding: 7px 10px 10px 10px + + &:hover + background-image: url(/images/level/code_toolbar_submit_button_zazz.png) + border: 0 + + &:active + background-image: url(/images/level/code_toolbar_submit_button_zazz_pressed.png) + padding: 9px 8px 8px 12px + border: 0 + + + diff --git a/app/styles/modal/auth.sass b/app/styles/modal/auth.sass deleted file mode 100644 index 37c2647f8..000000000 --- a/app/styles/modal/auth.sass +++ /dev/null @@ -1,28 +0,0 @@ -#auth-modal - .network-login - float: left - width: 100px - text-align: left - - #github-login-button - position: relative - top: -1px - border-radius: 5px - img - width: 16px - margin: 0 5px 0 -5px - - #gplus-login-button - position: relative - top: 1px - - #recover-account-wrapper - float: right - - .modal-footer - height: 70px - padding: 20px 10px - border-top: 1px solid darkgray - - .btn - margin-right: 10px diff --git a/app/styles/modal/model.sass b/app/styles/modal/model-modal.sass similarity index 100% rename from app/styles/modal/model.sass rename to app/styles/modal/model-modal.sass diff --git a/app/styles/modal/recover.sass b/app/styles/modal/recover-modal.sass similarity index 100% rename from app/styles/modal/recover.sass rename to app/styles/modal/recover-modal.sass diff --git a/app/styles/modal/revert.sass b/app/styles/modal/revert-modal.sass similarity index 100% rename from app/styles/modal/revert.sass rename to app/styles/modal/revert-modal.sass diff --git a/app/styles/modal/save_version.sass b/app/styles/modal/save-version-modal.sass similarity index 100% rename from app/styles/modal/save_version.sass rename to app/styles/modal/save-version-modal.sass diff --git a/app/styles/modal/subscribe-modal.sass b/app/styles/modal/subscribe-modal.sass new file mode 100644 index 000000000..3c3bf89b5 --- /dev/null +++ b/app/styles/modal/subscribe-modal.sass @@ -0,0 +1,248 @@ +@import "app/styles/mixins" +@import "app/styles/bootstrap/variables" + +#subscribe-modal + + //- Clear modal defaults + .modal-dialog + margin: 60px auto 0 auto + padding: 0 + width: 746px + height: 520px + background: none + + + //- Background + #subscribe-background + position: absolute + top: -61px + left: 0px + + //- Header + h1 + position: absolute + left: 170px + top: 25px + margin: 0 + width: 410px + text-align: center + color: rgb(254,188,68) + font-size: 38px + text-shadow: black 4px 4px 0, black -4px -4px 0, black 4px -4px 0, black -4px 4px 0, black 4px 0px 0, black 0px -4px 0, black -4px 0px 0, black 0px 4px 0, black 6px 6px 6px + font-variant: normal + text-transform: uppercase + + + //- Close modal button + + #close-modal + position: absolute + left: 568px + top: 17px + width: 60px + height: 60px + color: white + text-align: center + font-size: 30px + padding-top: 15px + cursor: pointer + @include rotate(-3deg) + + &:hover + color: yellow + + //- Popovers + + .popover + z-index: 1050 + min-width: 400px + + h3 + background: transparent + border: 0 + font-size: 30px + color: black + + //- Sales image + + .subscribe-image + position: absolute + top: 114px + right: 65px + + //- Feature comparison table + + .comparison-blurb + position: absolute + left: 10% + top: 132px + width: 450px + background: rgba(0, 0, 0, 0.0) + font-weight: normal + line-height: 18px + color: black + font-family: $headings-font-family + font-size: 18px + + .comparison-table + position: absolute + left: 10% + top: 160px + width: 450px + background: rgba(0, 0, 0, 0.0) + border-width: 0px + .free-cell + border-right-width: 1px + thead + tr + th + font-size: 24px + font-variant: small-caps + font-family: "Open Sans Condensed", "Helvetica Neue", Helvetica, Arial, sans-serif + font-weight: 700 + line-height: 1.1 + color: #317EAC + padding: 4px + border-width: 0px + border-color: rgba(85, 85, 85, 0.1) + tbody + font-size: 14px + .center-ok + text-align: center + tr + td + padding: 3px + border-width: 0px + border-top-width: 1px + border-color: rgba(85, 85, 85, 0.1) + + //- Parent info popover link + + #parents-info + position: absolute + left: 38px + top: 389px + text-decoration: underline + cursor: pointer + font-weight: bold + line-height: 18px + color: black + font-family: $headings-font-family + font-size: 18px + + .popover-title + line-height: 26px + + //- Payment methods info popover link + + #payment-methods-info + position: absolute + right: 38px + top: 389px + text-decoration: underline + cursor: pointer + font-weight: bold + line-height: 18px + color: black + font-family: $headings-font-family + font-size: 18px + + //- Purchase button + + .purchase-button + position: absolute + right: 24px + width: 400px + height: 70px + top: 430px + font-size: 32px + line-height: 42px + border-style: solid + border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round + border-width: 14px 20px 20px 20px + color: darken(white, 5%) + + span + pointer-events: none + + &:hover + border-image: url(/images/level/code_toolbar_submit_button_zazz.png) 14 20 20 20 fill round + color: white + + &:active + border-image: url(/images/level/code_toolbar_submit_button_zazz_pressed.png) 14 20 20 20 fill round + padding: 2px 0 0 2px + color: white + + //- Parent button + //- TODO: Add hover and active effects + + .parent-button + position: absolute + left: 24px + width: 250px + height: 70px + top: 430px + font-size: 28px + line-height: 38px + border-style: solid + border-image: url(/images/common/button-background-warning-disabled.png) 14 20 20 20 fill round + border-width: 14px 20px 20px 20px + color: darken(white, 5%) + + .email-parent-form + .email_invalid + color: red + display: none + .email-parent-complete + display: none + + //- Errors + + .alert + position: absolute + left: 10% + width: 80% + top: 20px + border: 5px solid gray + + +html.no-borderimage #subscribe-modal + .purchase-button + border: 0 + background-image: url(/images/level/code_toolbar_submit_button_active.png) + background-size: 100% 100% + padding: 7px 10px 10px 10px + + &:hover + background-image: url(/images/level/code_toolbar_submit_button_zazz.png) + border: 0 + + &:active + background-image: url(/images/level/code_toolbar_submit_button_zazz_pressed.png) + padding: 9px 8px 8px 12px + border: 0 + +body[lang='fr'] + #subscribe-modal .parent-button + font-size: 21px + + #subscribe-modal .comparison-table tbody + font-size: 13px + +body[lang='de-DE'] + #subscribe-modal .comparison-blurb + font-size: 16px + + #subscribe-modal .comparison-table tbody + font-size: 12px + +body[lang='pt-PT'] + #subscribe-modal .comparison-blurb + font-size: 16px + + #subscribe-modal .comparison-table tbody + font-size: 12px + + #subscribe-modal .parent-button + font-size: 18px diff --git a/app/styles/modal/wizard_settings.sass b/app/styles/modal/wizard_settings.sass deleted file mode 100644 index 7ba94a9c3..000000000 --- a/app/styles/modal/wizard_settings.sass +++ /dev/null @@ -1,17 +0,0 @@ -#wizard-settings-modal - color: black - - #wizard-settings-view #color-settings - width: 480px - - canvas - margin: 0px auto 20px - display: block - float: none - background: white - - #wizard-settings-name-wrapper - padding-left: 0px !important - - .help-block - text-align: center diff --git a/app/styles/play.sass b/app/styles/play.sass deleted file mode 100644 index da80b16f8..000000000 --- a/app/styles/play.sass +++ /dev/null @@ -1,63 +0,0 @@ -@import "app/styles/mixins" -@import "app/styles/bootstrap/variables" - -#play-view - .row - margin: 20px 0px 0px 0px - - .campaign-container - margin-bottom: 40px - - .campaign-description - font-style: italic - margin-top: -10px - - a[disabled] .level - opacity: 0.7 - - a.complete h3:after - content: " - Complete!" - color: green - - a.started h3:after - content: " - Started" - color: desaturate(green, 50%) - - .level - @include box-sizing(border-box) - border: 1px solid transparent - - &:hover, &:focus - border: 1px solid cyan - background-color: rgba(128, 192, 212, 0.25) - - .level-image - float: left - - .level-info - float: left - width: 330px - margin-left: 20px - - h3 - margin-top: 0 - margin-bottom: 0px - - .level-description - color: black - text-shadow: 0 1px 0 white - - -.modal.play-modal - .modal-header - border: 0 - text-align: center - padding: 0 - margin: 0 25px - - h3 - margin-bottom: 0 - - .modal-body - padding-top: 0 - diff --git a/app/styles/play/campaign-view.sass b/app/styles/play/campaign-view.sass new file mode 100644 index 000000000..10fb60465 --- /dev/null +++ b/app/styles/play/campaign-view.sass @@ -0,0 +1,624 @@ +@import "app/styles/mixins" +@import "app/styles/bootstrap/variables" + +$mapHeight: 1536 +$mapWidth: 2350 +$levelDotWidth: 2% +$levelDotHeight: $levelDotWidth * $mapWidth / $mapHeight +$levelDotZ: $levelDotHeight * 0.25 +$levelDotHoverZ: $levelDotZ * 2 +$levelDotShadowWidth: 0.8 * $levelDotWidth +$levelDotShadowHeight: 0.8 * $levelDotHeight +$levelClickRadius: 40px +$gameControlSize: 80px +$gameControlMargin: 30px + ++keyframes(levelStartedPulse) + from + @include box-shadow(0px 0px 4px #333) + margin-bottom: -$levelDotHeight / 3 + $levelDotZ + 50% + @include box-shadow(0px 0px 22px skyblue) + margin-bottom: -$levelDotHeight / 3 + ($levelDotHoverZ + $levelDotZ) / 2 + to + @include box-shadow(0px 0px 4px #333) + margin-bottom: -$levelDotHeight / 3 + $levelDotZ + +#campaign-view + width: 100% + height: 100% + position: absolute + + .gradient + position: absolute + z-index: 0 + + &.horizontal-gradient + left: 0 + right: 0 + height: 3% + + &.vertical-gradient + top: 0 + bottom: 0 + width: 3% + + &.top-gradient + top: 0 + + &.right-gradient + right: 0 + + &.bottom-gradient + bottom: 0 + + &.left-gradient + left: 0 + + .map + position: relative + + .map-background + width: 100% + height: 100% + background-size: 100% + @include user-select(none) + + .level, .level-shadow + position: absolute + border-radius: 50% + -webkit-transform: scaleY(0.75) + transform: scaleY(0.75) + + .level + z-index: 2 + width: $levelDotWidth + height: $levelDotHeight + margin-left: -0.5 * $levelDotWidth + margin-bottom: -$levelDotHeight / 3 + $levelDotZ + border: 2px groove white + @include transition(margin-bottom 0.5s ease) + + &.disabled, &.locked + background-image: url(/images/pages/game-menu/lock.png) + background-size: 75% + background-repeat: no-repeat + background-position: 50% 50% + opacity: 0.7 + + a + cursor: default + + &.next + width: 2 * $levelDotWidth + height: 2 * $levelDotHeight + margin-left: -0.5 * 2 * $levelDotWidth + margin-bottom: -2 * $levelDotHeight / 3 + 2 * $levelDotZ + + &.started, &.next + border: 3px solid lightgreen + @include box-shadow(0px 0px 35px skyblue) + + // Would be cool, but kills performance, since we have to re-render all the time. + //&:not(:hover) + // -webkit-animation-name: levelStartedPulse + // -webkit-animation-duration: 3s + // -webkit-animation-iteration-count: infinite + + &.complete + border: 3px solid gold + @include box-shadow(0px 0px 35px skyblue) + + .level-difficulty-banner-text + position: absolute + bottom: 170% + pointer-events: none + color: rgb(246, 208, 2) + text-shadow: 0px 1px 0px black + font-size: 1.8vw + z-index: 1 + width: 100% + text-align: center + + img.banner + position: absolute + bottom: 38% + left: -50% + width: 200% + pointer-events: none + + img.star + width: 100% + bottom: 7% + position: absolute + pointer-events: none + + .glyphicon-star + position: absolute + color: lightblue + font-size: 21px + left: 1.5px + + &.started .glyphicon-star + left: 0.5px + + img.hero-portrait + width: 120% + height: auto + bottom: 75% + left: 75% + margin-left: 0 + margin-bottom: 0 + + img.hero-portrait + position: absolute + border: 1px solid black + border-radius: 50% + background: white + width: $levelDotWidth * 1.5 + height: $levelDotHeight * 1.5 / 1.75 + margin-left: -0.5 * $levelDotWidth * 1.5 + margin-bottom: -$levelDotHeight / 3 * 1.5 / 1.75 + + .level-shadow + z-index: 1 + width: $levelDotShadowWidth + height: $levelDotShadowHeight + margin-left: -0.5 * $levelDotShadowWidth + margin-bottom: -$levelDotShadowHeight / 3 + background-color: black + @include box-shadow(0px 0px 10px black) + @include opacity(0.75) + + &.next + width: 2 * $levelDotShadowWidth + height: 2 * $levelDotShadowHeight + margin-left: -0.5 * 2 * $levelDotShadowWidth + margin-bottom: -2 * $levelDotShadowHeight / 3 + + .level:hover + // TODO: This rotate stops Firefox from flickering, but also disables the scaleY(0.75) + // TODO: The dot looks like it's jumping. + // TODO: -moz-transform: scaleY(0.75) didn't do anything + // TODO: Does not break Chrome's oval. + -moz-transform: rotate(0) + margin-bottom: -$levelDotHeight / 3 + $levelDotHoverZ + @include box-shadow(0px 0px 35px skyblue) + + &.next + margin-bottom: -2 * $levelDotHeight / 3 + 2 * $levelDotHoverZ + + .level + a + display: block + padding: $levelClickRadius + margin-left: -0.5 * $levelClickRadius + margin-top: -0.5 * $levelClickRadius + border-radius: $levelClickRadius + + &.next a + padding: 2 * $levelClickRadius + margin-left: 2 * -0.5 * $levelClickRadius + margin-top: 2 * -0.5 * $levelClickRadius + border-radius: 2 * $levelClickRadius + + .tooltip + z-index: 2 + + .tooltip-arrow + display: none + + .level-info-container + display: none + position: absolute + z-index: 3 + width: 362px + //min-height: 179px + padding: 17px 20px 20px 20px + border: 0 + background: transparent url(/images/pages/play/level-info-background.png) no-repeat center center + background-size: 100% 100% + + .level-info + + h3 + color: rgb(232, 217, 87) + text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0 + padding: 0 2px + margin: 0 0 10px 31px + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + .level-description + color: black + + img + display: block + margin: 0px auto + max-width: 100% + + kbd + margin: 0 2px 2px 0 + display: inline-block + font-size: 12px + + .level-status + background: transparent url(/images/pages/play/level-info-status-spritesheet.png) no-repeat 0 0 + width: 60px + height: 60px + position: absolute + left: -15px + top: -15px + + &.complete .level-status + background-position: -60px 0 + + &.premium .level-status + background-position: -120px 0 + + &.complete.premium .level-status + background-position: -180px 0 + + .start-level + min-width: 200px + display: block + margin: 10px auto 0 auto + position: relative + + .badge + position: absolute + top: auto + left: auto + right: -25px + bottom: -25px + font-size: 20px + color: black + border: 1px solid black + background-color: rgb(232, 217, 87) + border-radius: 50% + opacity: 1 + padding: 3px 9px + + &.complete + .start-level, .view-solutions + min-width: calc(50% - 5px) + display: inline-block + width: calc(50% - 5px) + + .start-level + margin: 10px 0 0 5px + + .view-solutions + margin: 10px 5px 0 0 + + .campaign-switch + color: purple + position: absolute + z-index: 1 + font-size: 2vw + text-shadow: 0 0 0.3vw white, 0 0 0.3vw white + + &:hover + text-decoration: none + + .next-level-line + transform-origin: 0 100% + height: 8px + position: absolute + + .line + width: calc(100% - 12px - 10px) + float: left + margin-top: 2px + margin-bottom: 2px + height: 4px + background: repeating-linear-gradient(-45deg, #AF9F7D, #DFC89C 5px, #F1EAC0 5px, #AF9F7D 10px) + box-shadow: 0px 0px 4px black + + .point + width: 12px + float: left + margin-top: -4px + border-top: 8px solid transparent + border-bottom: 8px solid transparent + border-left: 12px solid lighten(#F1EAC0, 10%) + + .game-controls + position: absolute + right: 1% + bottom: 1% + z-index: 2 + + .btn + &:not(:first-child) + margin-left: $gameControlMargin + @media only screen and (max-height: 650px) + margin-left: 0 + width: $gameControlSize + height: $gameControlSize + + @media only screen and (max-height: 650px) + @include scale(0.67) + + background: url(/images/pages/play/menu_icons.png) no-repeat + + position: relative + img + position: absolute + left: 0 + top: 0 + width: 100% + height: 100% + + background-size: cover + @include transition(0.5s ease) + @include box-shadow(2px 2px 4px black) + border: 0 + border-radius: 12px + // IE9 shows a blank white button with this MS gradient filter in place + filter: none + + &:hover + @include box-shadow(0 0 12px #bbf) + + &:active, &.highlighted + @include box-shadow(0 0 20px white) + + &.items + background-position: (-1 * $gameControlSize) 0px + &.heroes + background-position: (-2 * $gameControlSize) 0px + &.achievements + background-position: (-3 * $gameControlSize) 0px + &.account + //background-position: (-4 * $gameControlSize) 0px + background-position: (-5 * $gameControlSize) 0px + &.settings + background-position: (-5 * $gameControlSize) 0px + &.gems + background-position: (-6 * $gameControlSize) 0px + &.poll + background-position: (-4 * $gameControlSize) 0px + + .tooltip + font-size: 24px + + .tooltip-arrow + display: none + + .user-status + position: absolute + bottom: 16px + left: 8px + text-align: center + font-size: 24px + color: white + text-shadow: 0px 2px 1px black, 0px -2px 1px black, -2px 0px 1px black, 2px 0px 1px black + height: 32px + line-height: 32px + + .user-status-line + position: relative + + button.btn.btn-illustrated + margin-left: 10px + min-width: 90px + height: 32px + color: white + + .gem, .player-hero-icon + position: absolute + top: 1px + + #gems-count + margin-left: 40px + + .player-level + margin-left: 5px + + .player-name + margin-left: 45px + + a + color: white + + $spriteSheetSize: 30px + + .player-hero-icon + background: transparent url(/images/pages/play/play-spritesheet.png) + background-size: cover + background-position: (-2 * $spriteSheetSize) 0 + display: inline-block + width: 30px + height: 30px + margin: 0px 2px + + .level-indicator + margin-left: 15px + color: white + display: inline-block + margin: 0 2px + + .player-hero-icon + margin-left: 10px + background-position: (-4 * $spriteSheetSize) 0 + + &.knight + background-position: (-5 * $spriteSheetSize) 0 + &.librarian + background-position: (-6 * $spriteSheetSize) 0 + &.ninja + background-position: (-7 * $spriteSheetSize) 0 + &.potion-master + background-position: (-8 * $spriteSheetSize) 0 + &.samurai + background-position: (-9 * $spriteSheetSize) 0 + &.trapper + background-position: (-10 * $spriteSheetSize) 0 + &.forest-archer + background-position: (-11 * $spriteSheetSize) 0 + &.sorcerer + background-position: (-12 * $spriteSheetSize) 0 + + .campaign-control-button + position: absolute + right: 1% + top: 1% + padding: 3px 8px + @include opacity(0.75) + + &:hover + @include opacity(1.0) + + .glyphicon + font-size: 32px + + #volume-button + .glyphicon + display: none + + &.vol-up .glyphicon.glyphicon-volume-up + display: inline-block + + &.vol-off .glyphicon.glyphicon-volume-off + display: inline-block + @include opacity(0.50) + &:hover + @include opacity(0.75) + + &.vol-down .glyphicon.glyphicon-volume-down + display: inline-block + + #back-button, #clear-storage-button + position: absolute + right: 70px + right: -webkit-calc(1% + 55px) + right: calc(1% + 55px) + + #campaign-status + position: absolute + left: 0 + top: 0 + width: 100% + margin: 0 + text-align: center + color: rgb(232, 217, 87) + font-size: 28px + text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0 + z-index: 30 + pointer-events: none + + .campaign-status-background + background: transparent url(/images/pages/play/campaign-banner.png) no-repeat center center + border-radius: 10px + padding-top: 30px + display: inline-block + min-width: 250px + height: 106px + + .campaign-name + line-height: 26px + + .levels-completed + font-size: 22px + + .particle-man + z-index: 2 + + .portal + position: relative + width: 100% + height: 100% + background: transparent url(/images/pages/play/portal-background.png) + display: flex + align-items: center + justify-content: center + overflow: hidden + + .portals + $campaignWidth: 317px + $campaignHeight: 634px + $campaignHoverScale: 1.2 + width: 6 * $campaignWidth + height: $campaignHeight * $campaignHoverScale + flex-wrap: nowrap + display: flex + overflow: hidden + + .campaign + width: $campaignWidth + height: $campaignHeight + margin-top: $campaignHeight * ($campaignHoverScale - 1) / 2 + background: transparent url(/images/pages/play/portal-campaigns.png) no-repeat 0 0 + display: inline-block + flex-shrink: 0 + position: relative + cursor: pointer + // http://easings.net/#easeOutBack plus tweaked a bit: http://cubic-bezier.com/#.11,.67,.08,1.42 + @include transition(0.25s cubic-bezier(0.11, 0.67, 0.8, 1.42)) + + &:hover + @include scale($campaignHoverScale) + + &.silhouette + @include filter(contrast(50%) brightness(65%)) + pointer-events: none + + &.locked + @include filter(contrast(80%) brightness(80%)) + pointer-events: none + + &.forest + background-position: (-1 * $campaignWidth) 0 + &.desert + background-position: (-2 * $campaignWidth) 0 + &.mountain + background-position: (-3 * $campaignWidth) 0 + &.glacier + background-position: (-4 * $campaignWidth) 0 + &.volcano + background-position: (-5 * $campaignWidth) 0 + + .campaign-label + position: absolute + top: 55% + width: 100% + text-align: center + + .campaign-name, .levels-completed, .campaign-locked + margin: 0 + color: white + text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0 + + .campaign-locked + margin: 32px 0 + + .campaign-description + margin: 0px 40px + background: transparent url(/images/level/popover_border_background.png) no-repeat + background-size: 100% 100% + padding: 12px + color: black + + .levels-completed + font-size: 22px + + .play-button + margin: 15px 0 + min-width: 100px + + #small-nav-logo + position: absolute + top: 1% + left: 1% + height: 60px + z-index: 1 + +body.ipad #campaign-view + // iPad only supports up to Kithgard Gates for now. + .campaign-switch + display: none + +body[lang='ru'] .portals h2 + font-size: 26px diff --git a/app/styles/play/ladder/ladder_tab.sass b/app/styles/play/ladder/ladder-tab-view.sass similarity index 76% rename from app/styles/play/ladder/ladder_tab.sass rename to app/styles/play/ladder/ladder-tab-view.sass index 2529f6280..b510c3655 100644 --- a/app/styles/play/ladder/ladder_tab.sass +++ b/app/styles/play/ladder/ladder-tab-view.sass @@ -1,6 +1,6 @@ #ladder-tab-view .name-col-cell - max-width: 150px + max-width: 100px white-space: nowrap overflow: hidden text-overflow: ellipsis @@ -51,7 +51,22 @@ td padding: 1px 2px - .code-language-cell + .code-language-cell, .hero-portrait-cell padding: 0 10px background: transparent url(/images/common/code_languages/javascript_icon.png) no-repeat center center height: 16px + background-size: 16px 16px + + .spectate-cell + cursor: pointer + + &:not(.selected) .glyphicon + display: none + opacity: 0.75 + + &:hover .glyphicon + display: inline-block + + .iconic-cell + text-align: center + diff --git a/app/styles/play/ladder/ladder.sass b/app/styles/play/ladder/ladder.sass index e64660fd2..aa04a6afb 100644 --- a/app/styles/play/ladder/ladder.sass +++ b/app/styles/play/ladder/ladder.sass @@ -1,20 +1,27 @@ #ladder-view - .main-content-area - background-color: whitesmoke - #level-column img - margin: -14px -12px 0px -12px - width: 100% - width: -webkit-calc(100% + 24px) - width: calc(100% + 24px) + #ladder-top + background-color: whitesmoke + margin: -14px -12px 20px -12px + padding-bottom: 30px + border-bottom: 1px solid #888 + + #level-column + padding-top: 14px + text-align: center + + img + margin-top: -14px + width: 100% h1 text-align: center .tournament-blurb - margin: -20px -12px 20px -12px - padding: 10px - background-color: white + margin-top: -10px + margin-bottom: 10px + padding: 10px 20px + background-color: whitesmoke h2 text-align: center @@ -56,13 +63,19 @@ .play-button margin-bottom: 10px background-image: none + color: white + font-size: 24px .spectate-button-container margin-top: 10px text-align: center + .btn.spectate-button + font-size: 18px + color: white + .name-col-cell - max-width: 300px + max-width: 100px text-overflow: ellipsis white-space: nowrap overflow: hidden diff --git a/app/styles/play/ladder/my_matches_tab.sass b/app/styles/play/ladder/my_matches_tab.sass index 13aca340d..6bc1a30b2 100644 --- a/app/styles/play/ladder/my_matches_tab.sass +++ b/app/styles/play/ladder/my_matches_tab.sass @@ -38,6 +38,12 @@ td padding: 1px 2px + .name-cell + max-width: 130px + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + .code-language-cell padding: 0 10px background: transparent url(/images/common/code_languages/javascript_icon.png) no-repeat center center diff --git a/app/styles/play/ladder_home.sass b/app/styles/play/ladder_home.sass index e9406ef18..38eb07af8 100644 --- a/app/styles/play/ladder_home.sass +++ b/app/styles/play/ladder_home.sass @@ -8,7 +8,7 @@ margin-bottom: 20px text-shadow: 2px 2px 5px black - &:hover div + &:hover div, &:hover .dynamic-level-name color: lighten($yellow, 20%) &:hover img @@ -19,9 +19,23 @@ .level-image width: 100% + .dynamic-level-name + position: absolute + z-index: 1 + top: 40px + width: 100% + text-align: center + + text-shadow: 0px 5px 5px black, -2px 0px 2px black, 2px 0px 2px black, 0px -2px 2px black + font-size: 72px + color: $yellow + font-family: $headings-font-family + font-variant: small-caps + @include transition(color .10s linear) + .overlay-text color: $yellow - font-family: Open Sans Condensed + font-family: $headings-font-family font-variant: small-caps @include transition(color .10s linear) diff --git a/app/styles/play/level.sass b/app/styles/play/level.sass index ae0ed4580..cd1ba311e 100644 --- a/app/styles/play/level.sass +++ b/app/styles/play/level.sass @@ -33,8 +33,8 @@ $level-resize-transition-time: 0.5s #playback-view $flags-width: 200px width: 90% - width: -webkit-calc(100% - $flags-width) - width: calc(100% - $flags-width) + width: -webkit-calc(100% - 200px) + width: calc(100% - 200px) left: $flags-width #code-area, #thang-hud, #goals-view display: none @@ -63,7 +63,6 @@ $level-resize-transition-time: 0.5s .level-content position: relative - overflow: hidden #canvas-wrapper top: 50px @@ -101,6 +100,19 @@ $level-resize-transition-time: 0.5s &.flag-color-selected cursor: crosshair + #ascii-surface + position: absolute + z-index: 3 + top: 0 + left: 0 + pointer-events: none + white-space: pre + font-family: Courier, monospace + color: white + background-color: rgba(14, 59, 130, 0.25) + transform-origin: 0 0 0 + line-height: 15px + min-width: 1024px position: relative @@ -115,6 +127,10 @@ $level-resize-transition-time: 0.5s top: 0px bottom: 0 @include transition(width $level-resize-transition-time ease-in-out, right $level-resize-transition-time ease-in-out) + z-index: 2 + + #game-area + position: relative overflow: hidden // Level Docs @@ -187,7 +203,7 @@ $level-resize-transition-time: 0.5s #play-footer text-align: center - font-family: "Open Sans Condensed" + font-family: $headings-font-family font-variant: small-caps font-size: 25px padding: 10px 0 @@ -206,16 +222,14 @@ $level-resize-transition-time: 0.5s @include opacity(1) @media screen and (min-aspect-ratio: 17/10) - display: none + &:not(.premium) + display: none - .hour-of-code-explanation - margin-top: 5px - color: white - font-size: 12px - - a - color: white - text-decoration: underline + #level-footer-shadow + position: absolute + width: 100% + height: 30px + background: linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 100%) #fullscreen-editor-background-screen background-color: black @@ -279,3 +293,14 @@ body.ipad #level-view canvas margin: 0 auto overflow: hidden + +#level-footer-background + display: none + position: absolute + background: transparent url(/images/level/footer_background.jpg) no-repeat + bottom: 0 + width: 100% + background-size: 100% 400px + height: 400px + z-index: -9001 + @include opacity(0.25) diff --git a/app/styles/play/level/control_bar.sass b/app/styles/play/level/control_bar.sass index b95ef88f5..a31844c8e 100644 --- a/app/styles/play/level/control_bar.sass +++ b/app/styles/play/level/control_bar.sass @@ -12,7 +12,7 @@ position: absolute z-index: 6 text-transform: uppercase - font-family: Open Sans Condensed + font-family: $headings-font-family font-weight: bold &.controls-disabled @@ -100,6 +100,7 @@ height: 60px margin: 0 auto padding: 8px + border-style: solid border-image: url(/images/level/control_bar_level_name_background.png) 30 fill round border-width: 0 15px 15px 15px text-align: center @@ -116,6 +117,13 @@ color: white font-size: 18px + .level-difficulty + font-size: 28px + color: $control-yellow-highlight + display: inline-block + @include rotate(-15deg) + vertical-align: middle + .multiplayer-area-container position: relative width: 100% @@ -128,6 +136,7 @@ height: 60px margin: 0 auto padding: 8px + border-style: solid border-image: url(/images/level/control_bar_level_name_background.png) 30 fill round border-width: 0 15px 15px 15px text-align: center diff --git a/app/styles/play/level/goals.sass b/app/styles/play/level/goals.sass index 1d8e4a06f..09e46f2f3 100644 --- a/app/styles/play/level/goals.sass +++ b/app/styles/play/level/goals.sass @@ -6,6 +6,7 @@ left: -15px top: -100px @include transition(0.5s ease-in-out) + border-style: solid border-image: url(/images/level/goals_background.png) 15 20 51 15 fill stretch border-width: 8px 10px 25px 8px margin: -8px -10px -25px -8px diff --git a/app/styles/play/level/hud.sass b/app/styles/play/level/hud.sass index be3502933..3f06d32ea 100644 --- a/app/styles/play/level/hud.sass +++ b/app/styles/play/level/hud.sass @@ -98,7 +98,7 @@ background-image: url(/images/level/hud_background.png) color: white text-transform: uppercase - font-family: Open Sans Condensed + font-family: $headings-font-family font-weight: bold font-size: 16px z-index: 4 @@ -151,7 +151,7 @@ background-position: (-6 * $iconSize) 0px &.prop-label-icon-maxSpeed background-position: (-7 * $iconSize) 0px - &.prop-label-icon-gold, &.prop-label-icon-bountyGold + &.prop-label-icon-gold, &.prop-label-icon-bountyGold, &.prop-label-icon-value background-position: (-8 * $iconSize) 0px .prop[name="health"] diff --git a/app/styles/play/level/loading.sass b/app/styles/play/level/loading.sass index 6f0fe1137..afbf6c6ec 100644 --- a/app/styles/play/level/loading.sass +++ b/app/styles/play/level/loading.sass @@ -9,7 +9,6 @@ background-size: contain #level-loading-view - color: blue width: 100% height: 100% position: absolute @@ -21,60 +20,116 @@ .loading-details position: absolute - top: 20px + top: 86px left: 50% - $WIDTH: 1000px + $WIDTH: 450px width: $WIDTH - min-height: 60px + height: 450px margin-left: (-$WIDTH / 2) z-index: 100 - background-color: rgba(220, 255, 230, 0.6) + background: transparent url(/images/level/code_editor_background.png) no-repeat + background-size: 100% 100% color: darkslategray font-size: 15px - border-radius: 30px - padding: 10px + padding: 80px 80px 40px 80px text-align: center // http://matthewlein.com/ceaser/ Bounce down a bit, then snap up. @include transition(top $UNVEIL_TIME cubic-bezier(0.285, -0.595, 0.670, -0.600)) - - .load-progress - position: absolute - left: 2% - top: 0px - opacity: 0.6 - width: 96% - height: 40px - margin: 10px auto 0 - - .progress - height: 100% - - .progress-bar - width: 1% - height: 100% - transition-duration: 0 - - &.active .progress-bar - transition-duration: 1.2s - - #tip-wrapper - position: relative - z-index: 2 - top: 10px + font-family: 'Open Sans Condensed' .level-loading-goals - margin: 30px auto 10px - width: 400px + text-align: left - .panel-heading + .goals-title + font-size: 32px + color: black + font-weight: bold + + li + font-size: 20px + color: black + + .progress-or-start-container + position: absolute + bottom: 95px + width: 325px + height: 80px + left: 48px + + .load-progress + width: 100% + height: 45px + margin: 20px auto 0 auto + + .progress + height: 100% + position: relative + background-color: transparent + @include box-shadow(none) + + .progress-background + width: 100% + height: 100% + background: transparent url(/images/level/loading_bar_back.png) no-repeat + background-size: 100% 100% + position: absolute + z-index: -1 + + .progress-bar-container + width: 75% + height: 100% + left: 10% + position: absolute + + .progress-bar + width: 1% + height: 100% + transition-duration: 0 + background: transparent url(/images/level/loading_bar_fill.png) no-repeat + background-size: 325px 100% + @include box-shadow(none) + + &.active .progress-bar + transition-duration: 1.2s + + .rim + position: absolute + left: 0 + top: 0 + width: 100% + height: 100% + background: transparent url(/images/level/loading_bar_rim.png) no-repeat + background-size: 100% 100% + + .start-level-button + display: none + width: 100% + height: auto + margin: 0px auto + font-size: 40px + line-height: 45px + font-variant: small-caps + text-transform: none + + .subscription-required + display: none + margin-top: -160px + color: black font-size: 24px - .list-group-item - font-size: 20px + .start-subscription-button + width: 100% + margin: 0px auto + font-size: 40px + font-variant: small-caps - .start-level-button - font-size: 40px - font-variant: small-caps + #tip-wrapper + position: absolute + z-index: 2 + bottom: 40px + left: 25px + width: 401px + color: #666 .left-wing, .right-wing width: 100% @@ -82,11 +137,15 @@ position: absolute .left-wing - @include wing-background('/images/level/loading_left_wing.png', right) + @include wing-background('/images/level/loading_left_wing_1920.jpg', right) + @media screen and ( max-width: 1366px ) + @include wing-background('/images/level/loading_left_wing_1366.jpg', right) left: -50% @include transition(all $UNVEIL_TIME ease) .right-wing - @include wing-background('/images/level/loading_right_wing.png', left) + @include wing-background('/images/level/loading_right_wing_1920.jpg', left) + @media screen and ( max-width: 1366px ) + @include wing-background('/images/level/loading_right_wing_1366.jpg', left) right: -50% @include transition(all $UNVEIL_TIME ease) diff --git a/app/styles/play/level/modal/hero-victory-modal.sass b/app/styles/play/level/modal/hero-victory-modal.sass index 2ca7c5c2b..418d58e94 100644 --- a/app/styles/play/level/modal/hero-victory-modal.sass +++ b/app/styles/play/level/modal/hero-victory-modal.sass @@ -2,55 +2,90 @@ @import "app/styles/bootstrap/variables" #hero-victory-modal + $hero-yellow-text: rgb(252, 201, 53) + //- Top-level modal container .modal-dialog - margin-top: 15px + margin-top: 0 padding-top: 0 + width: 750px + + .modal-content + position: relative + margin-top: -251px + + &.full-achievements + @media only screen and (max-height: 720px) + .modal-dialog + margin-top: -76px + #victory-header + background: transparent + @media only screen and (max-height: 640px) + .modal-dialog + margin-top: -130px + #victory-header + display: none //- Header .background-wrapper //background: url("/images/pages/play/level/modal/victory_modal_background.png") - width: 550px - background-color: transparent + width: 750px + background: transparent border: 0px solid transparent - border-width: 25px - border-image: url("/images/pages/play/level/modal/victory_modal_background.png") 25 fill round + border-style: solid + border-image: url("/images/pages/play/level/modal/victory_modal_border_background.png") 250 0 100 0 fill round + border-width: 250px 0 100px 0 border-radius: 12px - #victory-banner - position: absolute - left: -30px - z-index: 0 - - #victory-header - position: absolute - left: 135px - display: block - margin: 10px auto 0 - // http://easings.net/#easeOutBack plus tweaked a bit: http://cubic-bezier.com/#.18,.68,.75,2 - @include transition(0.5s cubic-bezier(0.18, 0.68, 0.75, 2)) - z-index: 1 - - &.out - @include scale(0) - .modal-header - height: 85px border: none + position: absolute + left: 188px + width: 378px + height: 134px + margin: 0 + padding: 0 + + #victory-header + position: relative + // http://easings.net/#easeOutBack plus tweaked a bit: http://cubic-bezier.com/#.18,.68,.75,2 + @include transition(0.5s cubic-bezier(0.18, 0.68, 0.75, 2)) + z-index: 1 + width: 100% + height: 100% + text-align: center + background: transparent url(/images/pages/play/level/modal/victory_hero.png) no-repeat + background-position: center -88px + &.out + @include scale(0) + + #victory-title + display: inline-block + margin-top: 74px + + h1 + text-transform: uppercase + text-align: center + color: $hero-yellow-text + font-size: 80px + margin: 0 + padding: 0 + text-shadow: black 8px 8px 0, black -8px -8px 0, black 8px -8px 0, black -8px 8px 0, black 8px 0px 0, black 0px -8px 0, black -8px 0px 0, black 0px 8px 0 //- Achievement panels .modal-body padding: 0 20px min-height: 30px + margin-top: 160px .achievement-panel - background: url("/images/pages/play/level/modal/achievement_plate.png") - width: 451px - height: 144px - margin: 5px auto + background: transparent url("/images/pages/play/level/modal/victory_modal_shelf.png") no-repeat center 73px + width: 824px + height: 127px + margin: 0px -37px 0px -57px position: relative @include transition-duration(1s) @@ -71,12 +106,16 @@ .achievement-description @include opacity(0.75) + z-index: 1 position: absolute text-align: center left: 95px right: 98px - top: 10px - color: white + top: 86px + color: $hero-yellow-text + font-weight: bold + text-transform: uppercase + font-family: $headings-font-family white-space: nowrap overflow: hidden text-overflow: ellipsis @@ -85,8 +124,7 @@ position: absolute left: 25px right: 23px - top: 41px - bottom: 18px + top: 0 @include flexbox() @include flex-justify-center() @@ -102,7 +140,7 @@ z-index: 1 @include transition(0.25s ease) - &.hero, &.item + &.hero, &.item, &.xp, &.gems background: url("/images/pages/play/level/modal/reward_plate_wide.png") width: 120px height: 83px @@ -185,28 +223,130 @@ .gems .pulse @include animation(rewardPulse 0.25s infinite) - //- Footer - - .modal-footer - padding-bottom: 0 - - p.sign-up-poke - color: white - .sign-up-button - float: right - margin: 2px 10px + //- Footer - totals #totals - color: white + width: 709px + height: 96px + background: transparent url(/images/pages/play/level/modal/xp_gems_parchment.png) + position: relative + text-align: left + + .total-wrapper + position: absolute + top: 18px + + &#xp-wrapper + left: 117px + width: 300px + + &#gem-wrapper + left: 529px + + .total-label + width: 90px + + .total-count + float: left + font-size: 45px + font-weight: bold + color: rgb(40, 33, 22) + margin-right: 12px + width: 78px + + &.four-digits + font-size: 40px + margin-top: 3px + + &.five-digits + font-size: 30px + margin-top: 10px + + .total-label + float: left + color: rgb(103, 92, 76) + text-transform: uppercase + font-weight: bold + font-family: $headings-font-family + font-size: 18px + margin-top: 13px + line-height: 18px + + .xp-bar-outer + background-color: rgb(40, 33, 22) + border: 4px solid rgb(40, 33, 22) + border-radius: 8px + width: 150px + height: 16px + margin-top: 3px + position: relative + float: left + + .xp-bar-already-achieved + background-color: rgb(166, 213, 88) + //background-color: white + border-radius: 8px + height: 100% + position: absolute + z-index: 1 + + .xp-bar-total + background-color: rgb(253, 171, 45) + border: 1px solid rgb(239, 177, 73) + border-radius: 8px + height: 100% + position: absolute + + + //- Footer - other stuff + + .modal-footer + // Negative bottom margin counteracts most of the extra the border image height. + margin: 0 0 -80px 0 + padding: 0 20px + text-align: center + + .sign-up-poke + width: 430px + + .sign-up-blurb + width: 175px + font-family: $headings-font-family + font-weight: bold + text-transform: uppercase + font-size: 18px + line-height: 18px + text-align: left + float: left + margin: 5px 0 0 5px + color: rgb(160, 150, 126) + + .sign-up-button + width: 250px + height: 60px + line-height: 30px + margin: 0 + float: left + + .leaderboard-button, .courses-button + height: 60px + line-height: 30px + margin: 0 10px + float: right + + .return-to-course-button + width: 258px + float: left .next-level-buttons float: right - .next-level-button - display: block - margin: 8px 10px - width: 150px + .next-level-button, .return-to-ladder-button + width: 258px + height: 60px + line-height: 30px + margin: 0 10px .ladder-submission-view display: inline-block @@ -214,26 +354,20 @@ .rank-button.btn-block display: inline-block - width: initial + width: auto padding-left: 19px padding-right: 19px .last-submitted float: none - .next-levels-prompt - display: none - margin: 30px -21px - - .btn - width: 30% - width: -webkit-calc(33.333333% - 10px) - width: calc(33.333333% - 10px) - margin: 5px - .hour-of-code-done clear: both - padding-top: 10px + margin: 5px auto 0 auto + padding: 10px + background-color: rgba(22, 34, 30, 0.5) + border-radius: 8px + display: inline-block strong color: white @@ -248,29 +382,103 @@ .text-link color: lighten(#0b63bc, 10%) + .offer + display: none + + p + color: white + + #victory-text + z-index: 1 + text-align: center + padding: 30px 13px 0 13px + margin-bottom: 35px + font-size: 24px + color: white + font-weight: bold + text-transform: uppercase + font-family: $headings-font-family + text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0 + + #level-feedback + color: $hero-yellow-text + font-weight: bold + text-transform: uppercase + font-size: 20px + font-family: $headings-font-family + padding: 0 13px 20px 13px + text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0 + + .rating + position: relative + margin-top: 5px + text-align: center + float: left + width: 50% + + .rating-label + margin-bottom: 8px + + i + cursor: pointer + padding: 5px + font-size: 48px + text-shadow: black 3px 3px 0, black -3px -3px 0, black 3px -3px 0, black -3px 3px 0, black 3px 0px 0, black 0px -3px 0, black -3px 0px 0, black 0px 3px 0 + + .review-label + margin-top: 5px + text-align: center + float: right + width: 50% + + .review + width: 100% + text-align: center + + textarea + float: right + width: 50% + height: 80px + box-sizing: border-box + background-color: rgba(255, 255, 255, 0.7) + font-size: 16px + + &:not(.with-achievements) + #totals + display: none + + &.with-achievements + #victory-text + display: none html.no-borderimage #hero-victory-modal + .modal-dialog + margin-top: 251px .background-wrapper border: 0 background: url("/images/pages/play/level/modal/victory_modal_background.png") - height: 650px - #victory-header - margin-top: 40px - left: 160px - #victory-banner - left: 0px - top: 40px - .modal-header - height: 110px - .modal-content - height: 650px - padding-bottom: 0 - .modal-footer - bottom: 20px + height: 713px + + &.full-achievements + @media only screen and (max-height: 720px) + .modal-dialog + margin-top: 175px + #victory-header + background: transparent + @media only screen and (max-height: 640px) + .modal-dialog + margin-top: 121px + #victory-header + display: none + body.ipad #hero-victory-modal // This animation is slow and wigs out on iPad and very old computers. .xp .pulse, .gems .pulse @include animation(none) + +body[lang='ru'], body[lang^='es-ES'], body[lang^='it'], body[lang^='hu'], body[lang^='mk-MK'], body[lang^='ja'], body[lang^='uk'] + #hero-victory-modal #totals .total-wrapper .total-label + font-size: 12px diff --git a/app/styles/play/level/modal/victory.sass b/app/styles/play/level/modal/victory.sass index 9a343b3ce..b820540e2 100644 --- a/app/styles/play/level/modal/victory.sass +++ b/app/styles/play/level/modal/victory.sass @@ -1,3 +1,6 @@ +@import "app/styles/mixins" +@import "app/styles/bootstrap/variables" + #level-victory-modal .victory-banner float: right @@ -16,10 +19,6 @@ float: right margin-left: 10px - .next-level-button, .world-map-button - float: right - margin-left: 10px - .rating float: left position: relative @@ -55,4 +54,4 @@ body.ipad #level-victory-modal .modal-body font-size: 30px - font-family: Open Sans Condensed + font-family: $headings-font-family diff --git a/app/styles/play/level/tome/cast_button.sass b/app/styles/play/level/tome/cast_button.sass index 4ecd74768..3a8a8c45c 100644 --- a/app/styles/play/level/tome/cast_button.sass +++ b/app/styles/play/level/tome/cast_button.sass @@ -46,6 +46,7 @@ width: 45% width: -webkit-calc(50% - 10px) width: calc(50% - 10px) + border-style: solid border-image: url(/images/level/code_toolbar_run_button_active.png) 14 20 20 20 fill round border-width: 7px 10px 10px 10px @@ -115,7 +116,7 @@ html.no-borderimage #cast-button-view border: 0 &.submit-button, &.done-button - background-image: url(/images/level/code_toolbar_submit_button_active_pressed.png) + background-image: url(/images/level/code_toolbar_submit_button_active.png) border: 0 &:active diff --git a/app/styles/play/level/tome/problem_alert.sass b/app/styles/play/level/tome/problem_alert.sass index aaba3a19d..d58a12dd1 100644 --- a/app/styles/play/level/tome/problem_alert.sass +++ b/app/styles/play/level/tome/problem_alert.sass @@ -15,6 +15,7 @@ text-shadow: none color: white word-wrap: break-word + border-style: solid border-image: url(/images/level/code_editor_error_background.png) 16 20 fill round border-width: 16px 20px @@ -62,6 +63,9 @@ &.alert-info border-image-source: url(/images/level/code_editor_info_background.png) + + #problem-alert-help-button + float: right html.no-borderimage .problem-alert diff --git a/app/styles/play/level/tome/spell.sass b/app/styles/play/level/tome/spell.sass index 39f65f6d4..416dbb21f 100644 --- a/app/styles/play/level/tome/spell.sass +++ b/app/styles/play/level/tome/spell.sass @@ -25,7 +25,8 @@ left: 0px height: 100% right: -10px - + border: 1px solid transparent + span.code-background border-width: 124px 76px 64px 40px border-image: url(/images/level/code_editor_background_border.png) 124 76 64 40 fill round @@ -62,6 +63,7 @@ overflow: visible // https://github.com/codecombat/codecombat/issues/1411#issuecomment-60492750 -- trying to make sure system defaults don't mess up our monospace font. font-family: Monaco, Menlo, Ubuntu Mono, Consolas, "source-code-pro", monospace !important + @include transition(height 0.25s ease-in-out) &.disabled @include opacity(0.8) @@ -131,38 +133,55 @@ @include animation(progress-bar-stripes 0.5s linear infinite) &:not(.user-code-problem) - .ace_gutter-cell.executing:not(.ace_error):not(.ace_warning):not(.ace_info):after - - // Experimenting with a larger executing-line-pointer - content: "\e072" - position: relative - top: -31px - left: -39px - display: inline-block - font-family: 'Glyphicons Halflings' - font-style: normal - font-weight: normal - line-height: 1 - color: white - text-shadow: 0 0 5px black, 0 0 5px black, 0 0 5px black - font-size: 39px - -webkit-font-smoothing: antialiased - -moz-osx-font-smoothing: grayscale + .ace_gutter-cell.executing:not(.ace_error):not(.ace_warning):not(.ace_info) + margin-left: 1px + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMjgwMTE3NDA3MjA2ODExOEE2REU4Q0M1MTM1MkIxRiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBQjVEQUNDMzQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBQjVEQUNDMjQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTU1MjE3RDIzMTIwNjgxMThEQkI4NTlBMjQ1QTEwOTUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDI4MDExNzQwNzIwNjgxMThBNkRFOENDNTEzNTJCMUYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7SazaGAAAAiElEQVR42mL8//8/AzUBEwOVweA3kAWboI2jCyhgDwBx4ZH9ey5Qy4UOQHweaHg/EAtQ08sFUIMDqBmGCkC8HmgoCCtQM1ICoK5toGYsg8KzHmjo+UGbDj8AcSMwORkSnQ7xgA3QtPmApISNBTyAGrSBGl6eAMSGxBhGyIVkZT3G0fKQYgAQYACL+C2ZM6PC7AAAAABJRU5ErkJggg==) + background-position: 0px center .ace_gutter-cell.executed:not(.ace_error):not(.ace_warning):not(.ace_info) margin-left: 1px background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANhJREFUeNpi/P//PwM1ARMDlcHgMrA428MAiANQBEFhSA4uynIXAOJ+dHFKXDgfiDdSxctAbzYAqQ+9U3ccQJdjIcMwByCVD8SGFEcK0DAFILUeiCcCXfeAIgOBhglADfsAxBNwqSPFy/1AbADEiUDXfSApHQJdcx+I9yPxE4AUCB8AGrYAn62M6HkZ6rX3UG4jEG8A4vNQviO2mMXrQqh3GqHcemi4gcACQobhixRQoMNiUQEaEY1k52WoKwuRhHAmE6KTDdCADdDwu4AvmRCMlOFfwAIEGAD4On+N4aXlhgAAAABJRU5ErkJggg==) background-position: 0px center - .ace_gutter-cell.comment-line - background-position: 0px center - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAPCAYAAADtc08vAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIJJREFUeNpiTF9y4j0DA4MAA5mAiRLNIMACY8yMsWAkRSPQ5Q1Aqp6JgUIwagAVDGAERsd/LOKGwGi9AJRLALL78aUVRhwp0RGIHUDxTNAFaIljP1TjBSA2gAofAOJAoIs+4E2JaACmeQFQYyK5gdhISDM2F3yA4kKg5gXExAJAgAEACuQiMh7vH10AAAAASUVORK5CYII=) + .ace_gutter-cell.entry-point:after + content: " " + display: inline-block + position: relative + left: -49px + width: 49px + top: -30px + height: 38px + overflow: visible + + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADEAAAAmCAYAAABtT3M/AAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAActpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+QWRvYmUgSW1hZ2VSZWFkeTwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KKS7NPQAAC75JREFUWAm9WQ1wVcUV3t1778vLIwEEBeUnpIJGEyGEWKsihQii1jLDzxBG618ZEFCwav/E1gG0jlNltGNHHMuMFoL8BBUpIpZUCD8hmkkIjxj5KY5Ap4KEQEzCS979235n37t5N+ElEmbSnblv95495+z5zjm7e3cfZz1fOIaQNMzs2XOeYty6w7acU5YlVq5bt/rghAkT9NLSUsfjIb7uFtFdge7yL126lECwOY/NezvUK/T6hUjajKgZXMhYNFxYeP8jAGDPnDmT7FB83dVP/NrlCF2qDIzTVqxY4c5fuHCsYOJNy+LOpIma079fmnP4iKOlBFumZmWNlO+/X1wKnRyABUCpqF3qGMTXo5HIyclR3tW4HG/bBhsxwnDH3vqx8fN7PwvMmD7AbWru6wjeumz69BkrYIsECJeAdwdAj4NIGOPqjitYei+LSUm4wmzc7R+KRx+6UkTtwbZtRxZMnTqtmPg3btzo0DxJyP5wq0cj4Q2vEh62uwoAzeEMKeVZmZ+3is+b00cLhq63IpHGmVOmTNnBmJrodn5+vuHJ/1DdhhhaOfKxXSh37SLxXSw7O7vbeQqPsvr6etJnQ3Ugtj555pho9GWO0yyzb1jO5s35jVG0Nt/895G9BZMn96qsr580vqrqX98TkKqqKsuT6qxWIIqLizXOObnI7ozxMumkk0kuzfZrD6UU2RZiNFeuzXxZzp39bGBd8Z3mZ9s35w68uk91fv4dE6qq9p7EvArU1tYS6k4LRwQUADAGTp+u+wUT8nbHcYKWZWk2HhMLumPbHImrHsu2GejMdmzm2A68aTPLtphjxdq2CzratkM8DoeuqOuYoxsbRdaPbxbu3ZM+FlJaiCwFiQIsME8YM4zj7Nz53/LiD3tbK/76jnHTTVfUR1pSx4fDZbU/BIRcwiorK8eYlv1heu8+wxoaWlgk0grDYJxpMYBgAATDyVg86h1A0CYQVFvoo9qOv7dvO+hvZWfqXHfiBCGmTtkkXZeiABA0yzkhwY80mK4fYxciz/Atn1xtPbf4L8bo0WlRIfretX9/xZ6ugPB9+w4MNgLOYd0IppXt22u65i7eK9SECJGfyFP0UKHxgJnjl6upSm94xw7g0akliAd126NJtDXXNUS/K1rl9deVQKXmaSUVagBquDLINO0onLWQl+wYYS9a+JqeldXCUlOHTT1woHJzZ3OE799/4K0r+vWbv3XrltbMIU8Ec7LJMKQxtAIIpoo3HuiqkNdA5hyD09DE573TC6GMy0CeUcKiFtyRrtQRrYExKU9/HAQhIW0S84Tzw2g9xsu+GOM88vAb2uBBX7ErrxozOxze/26yzxReU1Nz4nyDmXHkq0XuvXcf5pGWq2FUFFbFDIkBobh0Xvw8sXYCOL2TJIEm3Jypua6UkRfUOGh4A9CwkqWh/xDT9Id4uKbAmTZtJUJfzkeOHPP7mpr9r6CTUoFElJjgQmRgKZQDBuwTjtsP9GYmuJV4BLXtrh9B/Q4Tov2DBY9rmiMVHTr8AGLQMFy8xGJKVlEgm0HNxgJRJPNGbdVKSh6XPxrxMxcA/pybm/cyOl08BIDAYGy4x3VpMCQC9UALecfzYAxrHDJJJCueGz3fJKs7ynkyPnp7II2IUzYzzQ/kddcWifdWP8Tv+OkDdjhc/eyoUaNWxsUIjIghESI2hz0txEFt74k3iZy0eHIef7K6o6An46eD5pGlWi4a4eUbmRndLgcNfJ2/+cY0bWbhQuvgwYNzRo7MWRsXxQcNxOD1i4vfmxf3tqd48n6Zju32ErHQejxeX1yP5wMCwlgTE9oNLBqtkH3Sn2EvvXinvuCJP5g1NbX3Z2VlFZFo22eHp6fNF56mREfnLc99HWuS8GgdpT061XHjFa/XbhNFnrsXkO5Z0oyeYLoxnS3+3WaDuYvNt956+cHc3NydKp3a66fMchAd30PvXT3E21HGL+/1+3X4+z16Mj7VB5skUkvLwMbZi0l7PvvVk2NFRmYBC4fDCzpEwsWsDmGmpMZmNrxEqYaJ3mXx8yRr+2l+RZ3R/TwUJuITXMdm2AxbhmKvOcxCIZcPyxjITh5nvRIglMEmAAxmhk57haOMv5SB/DzJ2n6a38DO6DEe2jHgVDVnNea4/2WuUwcwJ1kobS1fVfSNtWf3ei0nJ3tjAoSSbAGI/viGGQrUWNfVWutFwpesbYlOtNjC4EXLb5jX9mo/AGonp8d0YiJANWU7xzJ7SFpWBZyL/cgo4u+uPmM+t/ip4PDhwz6vrf1qiQKRyJZ6yPXFTnkN4y6+nrECqw3Ds7CjFfF3P0+ytp/mV3ERPW4/AeBIH5pnLZHPAeKfLBBgzLTX8JUrvjGXv/p84MYbR1QcOnRsIukDCJrI8X0OSgRPB4h+ABGFIqwMcBdFhPjIK8lKgoe86/En2n6apyeWJglepVelNNEAAEeQ5qZPAeAfLCUli33f/Dx77fWwuervrwawR5RgiZ0ct0UDiLiFMoC8MKVm9JV6YBDa9M3vpUpy4z1AfiOTtRM0zxE0Wem8TR5P6KZ3IQJ04mONDR/hKLAFAMby03Xz5Asv7bY/2fK3QG7uyOJwuGaWBwC1o7vSrUtL631V3ekMVzfO80ikEt+dOFHSeJ7raZz4+PTt2jEg/tz2t0mKAoM65io06fuJY/VLS78NNOSIUkwcBCCIJfQsazi/CueX7SyQci//+nihu/i5j92KL4oN7AlvY0mdT9wodKpSX5O6FbV2ZGZeMyt84AH75IkXAkOGbGO2uY1GUwWOUnDIGK8d60n8KsPjr9T2ZD2ZeA1EOJTiPKRpOax337uQuiHwugqppqWyaOt/WH3dcua6e5kRmMkPfnmX++RTxezEN9v0vLz8P1VXVz0f107WtX0O665rv9LaemHWT269L1D2uTD7H90hQqEIFNHqpFYHGI9vKwxJc4TyNVbHDj6gK3TtaOpARLzUp0FScFcaIj3NlIMHbVOblqb1Bh37EY71mtYLE/goq/vuadh2CP0P8/KKW50HH12tDRmwl+Xl3fLr6uqK19BJ3qePPnraivJ3eXnFLIy3PjWUzk6dOscuXIioY6k6ksaPpbEjJ52vY8dUdTR1cK72HUsdx1XHVJyrUbvIbQfOoNpkdWelvHOC4FPvWy65uIcNGroC6ZOKz4kU2dxYzb47PZFp8JntzuU7d+c48+etwR7wLU51Qx+urPyiKNlhyEOh003HbbfdsmHPnj3V0dbWRcEgu0nXhGZZOrccyS3TxQUBjz2OQDpwgZTABQDolotLBNS4EAA4YQOEg0sDBRgXBQqgBU7XGgwsA4IpBmUWooP4iRAuB/oj/3eyM6cmI30Yi7Q8zrdtH2a/+MJq/eabW3EOyZxSUbFvKx1LcZ1EB/OkRS8sLHR27typjxs37ig4FiXlunwiRVouWrTgRV1P/SPWf1oxDM5SpK73YQ3nSuV3AJASZKzh+yf5pi0DrTWr38cENiKOkz5p375d5XRBgLunLq9s1GZXUFBAyukyVyBs7rJly5KavWTJEjXJ/Z3+JZIM9vfhblXDg8TCxEEP7Uiq8CDyfz07V/9Lnhpk8vSZp/mGD9Ksvbs/NYYPTzvT3CzHl5eXHu7qhsM/jgJBBBhz0YTxM1IbBnUkXdI7TtfxhZaWuOGYV6VIow3I93R5/ORc/t76AG7/ygIDB6Ydi0ZtACj59lIBkAFtIC7JmstlEliCEAS6KEDAlZaUQCY7dGQGX7NOmufqDgT69E2vOlsnC8rKtjXFr2a6TCG/Kf8fENgeKNCtUR0fl00AovP94Rly7QbTcqwTgWBq6vZNmzbdA8NkfBXqdBL7jffaPQoCV6NqEtiuqDQMmx37WpcHv5zmnDuf6m7eEhHBwFlDGMHVWCEfIYPiALp9H6z2CQ9RD9RqdSK9cx+bX5KSkjKp7qyLc0GUBYwmZjl8WfH6tUupn/5cof8mqN3dQjtgjxYsBuovrNG5oz4ybSddFxeCmhatcRwxf8P6996hfkSA099il2vI/wBor0wWej/CaAAAAABJRU5ErkJggg==) + + .ace_gutter-cell.entry-point:not(.next-entry-point):after + opacity: 0.5 .ace_marker-layer .ace_bracket // Override faint gray border-color: #BFF + .locked-code + border: 1px dashed rgba(53, 45, 34, 0.5) + background-color: transparent + @include transition(background-color 0.25s ease-in-out) + + &:after + content: "Locked" + opacity: 0 + display: block + text-align: right + @include transition(opacity 4s ease-in-out) + font-size: 24px + + &.pulsating + background-color: rgba(53, 45, 34, 0.5) + + &:after + opacity: 1 + // Decided it wasn't useful to show what can be hovered, since almost anything can, so we have to make it too faint to be useful if we don't want it to be really distracting. //.ace_identifier // border-bottom: 1px dotted rgba(0, 51, 255, 0.25) diff --git a/app/styles/play/level/tome/spell_debug.sass b/app/styles/play/level/tome/spell_debug.sass index 58d8b5f71..b2d3ca17a 100644 --- a/app/styles/play/level/tome/spell_debug.sass +++ b/app/styles/play/level/tome/spell_debug.sass @@ -7,8 +7,9 @@ min-width: 250px max-width: 400px padding: 10px - border-image: url(/images/level/popover_background.png) 18 fill round - border-width: 8px + border-style: solid + border-image: url(/images/level/popover_border_background.png) 16 12 fill round + border-width: 8px 6px .progress position: relative span diff --git a/app/styles/play/level/tome/spell_list.sass b/app/styles/play/level/tome/spell_list.sass index e988bfce0..b66cd5f82 100644 --- a/app/styles/play/level/tome/spell_list.sass +++ b/app/styles/play/level/tome/spell_list.sass @@ -9,8 +9,9 @@ left: 0% right: 10% padding: 4% - border-image: url(/images/level/popover_background.png) 18 fill round - border-width: 15px + border-style: solid + border-image: url(/images/level/popover_border_background.png) 16 12 fill round + border-width: 16px 12px html.no-borderimage #spell-list-view diff --git a/app/styles/play/level/tome/spell_list_entry.sass b/app/styles/play/level/tome/spell_list_entry.sass index 60f700c31..9599039cb 100644 --- a/app/styles/play/level/tome/spell_list_entry.sass +++ b/app/styles/play/level/tome/spell_list_entry.sass @@ -93,7 +93,7 @@ margin-top: 10px text-transform: uppercase display: inline-block - font-family: Open Sans Condensed + font-family: $headings-font-family font-weight: bold .method-label diff --git a/app/styles/play/level/tome/spell_list_entry_thangs.sass b/app/styles/play/level/tome/spell_list_entry_thangs.sass index 5f8a275f7..abe03b44a 100644 --- a/app/styles/play/level/tome/spell_list_entry_thangs.sass +++ b/app/styles/play/level/tome/spell_list_entry_thangs.sass @@ -11,8 +11,9 @@ max-height: 500px overflow: scroll padding: 4% - border-image: url(/images/level/popover_background.png) 18 fill round - border-width: 15px + border-style: solid + border-image: url(/images/level/popover_border_background.png) 16 12 fill round + border-width: 16px 12px .thang-avatar-view cursor: pointer diff --git a/app/styles/play/level/tome/spell_palette.sass b/app/styles/play/level/tome/spell_palette.sass index 64755f641..287f4e8e8 100644 --- a/app/styles/play/level/tome/spell_palette.sass +++ b/app/styles/play/level/tome/spell_palette.sass @@ -5,17 +5,23 @@ position: absolute left: 10px right: 10px - padding: 0 4px 10px 40px + padding: 0 4px 10px 3% background-color: transparent background-size: 100% 100% z-index: 2 - //overflow-y: auto + @include transition(top 0.25s ease-in-out, height 0.25s ease-in-out) + box-shadow: 10px 4px 4px black + overflow-y: hidden .code-palette-background width: 100% + height: 592px position: absolute left: 0px z-index: -1 + background: transparent url(/images/level/code_palette_wood_background.png) + background-size: 100% 592px + overflow: visible &.controls-disabled .code-palette-background @@ -37,7 +43,7 @@ margin-bottom: 3px ul.nav.nav-pills - margin-top: 15px + margin-top: 3% h4 margin-top: 2px @@ -48,11 +54,12 @@ &.multiple-tabs li:not(.active) a cursor: pointer - .tab-content - height: 80px - .nano-pane - width: 7px - right: 5px + &:not(.hero) + .tab-content + height: 80px + .nano-pane + width: 7px + right: 5px //.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus // background-color: lighten(rgb(230, 212, 146), 10%) @@ -61,6 +68,9 @@ display: inline-block margin-right: 3px vertical-align: top + + #spell-palette-help-button + margin: 3% 0px 4px &.hero .properties @include flexbox() @@ -68,6 +78,9 @@ @include flex-column() @include flex-align-content-start() + &.no-help + margin-top: 3% + .property-entry-item-group display: inline-block min-height: 38px @@ -94,6 +107,15 @@ width: -webkit-calc(100% - 38px) width: calc(100% - 38px) + &.shortenize.hero .properties + .property-entry-item-group + width: 175px + + .spell-palette-entry-view + width: 137px + width: -webkit-calc(100% - 38px) + width: calc(100% - 38px) + @media only screen and (max-width: 1100px) #spell-palette-view // Make sure we have enough room for at least two columns diff --git a/app/styles/play/level/tome/spell_palette_entry.sass b/app/styles/play/level/tome/spell_palette_entry.sass index 7ddba6bb9..f93194b80 100644 --- a/app/styles/play/level/tome/spell_palette_entry.sass +++ b/app/styles/play/level/tome/spell_palette_entry.sass @@ -50,13 +50,15 @@ body:not(.dialogue-view-active) .spell-palette-popover.popover right: 45% min-width: 500px + margin-top: -17% .spell-palette-popover.popover // Only those popovers which are our direct children (spell documentation) max-width: 600px padding: 0 - border-image: url(/images/level/popover_background.png) 29 39 fill stretch - border-width: 15px 20px + border-style: solid + border-image: url(/images/level/popover_border_background.png) 16 12 fill round + border-width: 16px 12px @include box-shadow(0 0 0 #000) // Jiggle animation diff --git a/app/styles/play/level/tome/spell_toolbar.sass b/app/styles/play/level/tome/spell_toolbar.sass index a29c2a137..1445b3055 100644 --- a/app/styles/play/level/tome/spell_toolbar.sass +++ b/app/styles/play/level/tome/spell_toolbar.sass @@ -82,9 +82,10 @@ position: absolute z-index: 10 pointer-events: none - border-image: url(/images/level/popover_background.png) 29 39 fill stretch + border-style: solid + border-image: url(/images/level/popover_border_background.png) 16 12 fill round padding: 0 - border-width: 15px 20px + border-width: 16px 12px font-variant: small-caps text-overflow: ellipsis font-size: 13px diff --git a/app/styles/play/level/tome/tome.sass b/app/styles/play/level/tome/tome.sass index 511a3fa14..d839b7a87 100644 --- a/app/styles/play/level/tome/tome.sass +++ b/app/styles/play/level/tome/tome.sass @@ -4,6 +4,7 @@ #tome-view height: 100% margin-bottom: -20px + overflow: hidden > .popover // Only those popovers which are our direct children (spell documentation) @@ -37,8 +38,9 @@ .popover padding: 10px 10px 30px 10px - border-image: url(/images/level/popover_background.png) 18 fill round - border-width: 15px + border-style: solid + border-image: url(/images/level/popover_border_background.png) 16 12 fill round + border-width: 16px 12px @include box-shadow(0 0 0 #000) h1:not(.not-code), h2:not(.not-code), h3:not(.not-code), h4:not(.not-code), h5:not(.not-code), h6:not(.not-code) diff --git a/app/styles/game-menu/game-menu-modal.sass b/app/styles/play/menu/game-menu-modal.sass similarity index 94% rename from app/styles/game-menu/game-menu-modal.sass rename to app/styles/play/menu/game-menu-modal.sass index 2707d39b0..a9390ea01 100644 --- a/app/styles/game-menu/game-menu-modal.sass +++ b/app/styles/play/menu/game-menu-modal.sass @@ -1,3 +1,4 @@ +@import "app/styles/bootstrap/variables" @import "app/styles/mixins" #game-menu-modal @@ -67,7 +68,7 @@ color: rgb(195,153,124) font-weight: bold padding: 14px 20px - font-family: Open Sans Condensed + font-family: $headings-font-family text-transform: uppercase .glyphicon diff --git a/app/styles/play/menu/guide-view.sass b/app/styles/play/menu/guide-view.sass new file mode 100644 index 000000000..503097adf --- /dev/null +++ b/app/styles/play/menu/guide-view.sass @@ -0,0 +1,54 @@ +#guide-view, #settings-treema .treema-markdown + .nav-tabs + height: 41px + + .tab-content + padding-top: 20px + margin-bottom: 50px + + li:not(.active) a[data-toggle="tab"] + cursor: pointer + + img + display: block + margin: 0 auto + + img + em + display: block + margin: 0 auto + text-align: center + + hr + border-color: #5c5c5c + width: 80% + + table + width: 80% + margin: 20px 10% + + //- Purchase button + + .purchase-button + position: absolute + left: 73px + width: 600px + height: 70px + top: 430px + font-size: 32px + line-height: 42px + border-style: solid + border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round + border-width: 14px 20px 20px 20px + color: darken(white, 5%) + + span + pointer-events: none + + &:hover + border-image: url(/images/level/code_toolbar_submit_button_zazz.png) 14 20 20 20 fill round + color: white + + &:active + border-image: url(/images/level/code_toolbar_submit_button_zazz_pressed.png) 14 20 20 20 fill round + padding: 2px 0 0 2px + color: white diff --git a/app/styles/game-menu/inventory-modal.sass b/app/styles/play/menu/inventory-modal.sass similarity index 83% rename from app/styles/game-menu/inventory-modal.sass rename to app/styles/play/menu/inventory-modal.sass index c40708245..c7bf281de 100644 --- a/app/styles/game-menu/inventory-modal.sass +++ b/app/styles/play/menu/inventory-modal.sass @@ -37,6 +37,20 @@ $itemSlotGridHeight: 51px left: -8px + //- Header + + h1 + position: absolute + left: 109px + top: 22px + margin: 0 + width: 255px + text-align: center + color: rgb(254,188,68) + font-size: 38px + text-shadow: black 4px 4px 0, black -4px -4px 0, black 4px -4px 0, black -4px 4px 0, black 4px 0px 0, black 0px -4px 0, black -4px 0px 0, black 0px 4px 0 + + //- Gems count #gems-count-container @@ -149,8 +163,8 @@ $itemSlotGridHeight: 51px background-position: (-4 * $itemSlotInnerWidth) 0px &[data-slot="minion"] - left: 10px + ($itemSlotGridWidth * 0) - top: 15px + ($itemSlotGridHeight * 0) + left: 10px + ($itemSlotGridWidth * 5) + top: 15px + ($itemSlotGridHeight * 1) .placeholder background-position: (-1 * $itemSlotInnerWidth) 0px @@ -158,15 +172,11 @@ $itemSlotGridHeight: 51px left: 10px + ($itemSlotGridWidth * 0) top: 15px + ($itemSlotGridHeight * 0) .placeholder - background-position: (-3 * $itemSlotInnerWidth) 0px - - // Only for wizards... - //&[data-slot="spellbook"] .placeholder - // background-position: (-2 * $itemSlotInnerWidth) 0px + background-position: (-2 * $itemSlotInnerWidth) 0px &[data-slot="wrists"] left: 10px + ($itemSlotGridWidth * 0) - top: 15px + ($itemSlotGridHeight * 0) + top: 15px + ($itemSlotGridHeight * 2) .placeholder background-position: (-5 * $itemSlotInnerWidth) 0px @@ -184,7 +194,7 @@ $itemSlotGridHeight: 51px &[data-slot="torso"] left: 10px + ($itemSlotGridWidth * 5) - top: 15px + ($itemSlotGridHeight * 2) + top: 15px + ($itemSlotGridHeight * 3) .placeholder background-position: (-8 * $itemSlotInnerWidth) 0px @@ -214,7 +224,7 @@ $itemSlotGridHeight: 51px &[data-slot="head"] left: 10px + ($itemSlotGridWidth * 5) - top: 15px + ($itemSlotGridHeight * 1) + top: 15px + ($itemSlotGridHeight * 2) .placeholder background-position: (-13 * $itemSlotInnerWidth) 0px @@ -226,19 +236,19 @@ $itemSlotGridHeight: 51px &[data-slot="gloves"] left: 10px + ($itemSlotGridWidth * 0) - top: 15px + ($itemSlotGridHeight * 2) + top: 15px + ($itemSlotGridHeight * 3) .placeholder background-position: (-15 * $itemSlotInnerWidth) 0px &[data-slot="left-hand"] left: 10px + ($itemSlotGridWidth * 5) - top: 15px + ($itemSlotGridHeight * 3) + top: 15px + ($itemSlotGridHeight * 4) .placeholder background-position: (-16 * $itemSlotInnerWidth) 0px - + &[data-slot="right-hand"] left: 10px + ($itemSlotGridWidth * 0) - top: 15px + ($itemSlotGridHeight * 3) + top: 15px + ($itemSlotGridHeight * 4) .placeholder background-position: (-17 * $itemSlotInnerWidth) 0px @@ -258,6 +268,18 @@ $itemSlotGridHeight: 51px border: 2px solid black background-color: white + &.Wizard #equipped .item-slot + &[data-slot="right-hand"] .placeholder + background-position: (-20 * $itemSlotInnerWidth) 0px + &[data-slot="left-hand"] .placeholder + background-position: (-3 * $itemSlotInnerWidth) 0px + + &.Ranger #equipped .item-slot + &[data-slot="right-hand"] .placeholder + background-position: (-18 * $itemSlotInnerWidth) 0px + &[data-slot="left-hand"] .placeholder + background-position: (-19 * $itemSlotInnerWidth) 0px + //- dragging styling @@ -320,7 +342,7 @@ $itemSlotGridHeight: 51px position: relative width: 60px - &:not(.equipped):not(.restricted):not(.silhouette) + &:not(.equipped):not(.restricted) cursor: pointer &:hover @@ -347,18 +369,6 @@ $itemSlotGridHeight: 51px padding: 0 @include transition(0.1s ease) - .required-level - @include opacity(0) - @include transition(0.6s ease-in) - pointer-events: none - font-size: 14px - text-align: center - width: 100% - bottom: 20px - top: inherit - position: absolute - color: white - &.active background-color: rgb(81,153,236) @@ -402,16 +412,6 @@ $itemSlotGridHeight: 51px &.locked:not(:hover) @include filter(contrast(75%)) - - &.silhouette - img - @include filter(contrast(25%) brightness(25%)) - opacity: 0.5 - - &:hover - .required-level - @include opacity(1) - text-shadow: 0 1px 0 black, 1px 0 0 black, 0 -1px 0 black, -1px 0 0 black //- Hero/Play buttons @@ -485,13 +485,19 @@ $itemSlotGridHeight: 51px position: absolute z-index: 12 + &.female + left: 80px + bottom: 31px + + &.Ranger + left: -7px + &.male left: 65px bottom: 31px - &.female - left: 80px - bottom: 31px + &.Ranger + left: -16px #hero-image-head z-index: 16 @@ -500,12 +506,12 @@ $itemSlotGridHeight: 51px #hero-image-thumb z-index: 16 - &.female + &.female:not(.Ranger) @include rotate(-15deg) left: 66px bottom: 54px - &.male + &.male:not(.Ranger) @include rotate(-15deg) left: 53px bottom: 54px @@ -525,7 +531,10 @@ $itemSlotGridHeight: 51px &.feet z-index: 13 - &.right-hand + &.male.Ranger + bottom: 45px + + &.right-hand:not(.Ranger) @include rotate(-15deg) &.female left: 66px @@ -534,7 +543,18 @@ $itemSlotGridHeight: 51px &.male left: 55px bottom: 63px - @include rotate(-15deg) + + &.right-hand.female.Ranger[src="/file/db/thang.type/53f4e6e3d822c23505b74f42/warrior_female.png"] + // Special-case Builder's Hammer, since we don't yet have ranger-specific hand exports for it + @include rotate(180deg) + left: -95px + bottom: -89px + + &.right-hand.male.Ranger[src="/file/db/thang.type/53f4e6e3d822c23505b74f42/warrior_male.png"] + // Special-case Builder's Hammer, since we don't yet have ranger-specific hand exports for it + @include rotate(180deg) + left: -138px + bottom: -55px &.left-hand z-index: 17 @@ -542,22 +562,35 @@ $itemSlotGridHeight: 51px &.torso z-index: 14 + &.male-back, &.female-back + z-index: 11 + &.gloves z-index: 15 &.female - &.female-thumb + &.female-thumb, &.female-ranger-thumb z-index: 16 - @include rotate(-15deg) - left: 66px - bottom: 54px + &:not(.Ranger) + @include rotate(-15deg) + left: 66px + bottom: 54px &.male - &.male-thumb + &.male-thumb, &.male-ranger-thumb z-index: 16 - @include rotate(-15deg) - left: 53px - bottom: 54px + &:not(.Ranger) + @include rotate(-15deg) + left: 53px + bottom: 54px &.head z-index: 17 + + &.Ranger:not(.feet) + &.female + left: -7px + + &.male + left: -16px + diff --git a/app/styles/game-menu/item-view.sass b/app/styles/play/menu/item-view.sass similarity index 100% rename from app/styles/game-menu/item-view.sass rename to app/styles/play/menu/item-view.sass diff --git a/app/styles/game-menu/multiplayer-view.sass b/app/styles/play/menu/multiplayer-view.sass similarity index 100% rename from app/styles/game-menu/multiplayer-view.sass rename to app/styles/play/menu/multiplayer-view.sass diff --git a/app/styles/game-menu/options-view.sass b/app/styles/play/menu/options-view.sass similarity index 100% rename from app/styles/game-menu/options-view.sass rename to app/styles/play/menu/options-view.sass diff --git a/app/styles/game-menu/save-load-view.sass b/app/styles/play/menu/save-load-view.sass similarity index 100% rename from app/styles/game-menu/save-load-view.sass rename to app/styles/play/menu/save-load-view.sass diff --git a/app/styles/play/modal/buy-gems-modal.sass b/app/styles/play/modal/buy-gems-modal.sass index e147d6041..6dcbbaa72 100644 --- a/app/styles/play/modal/buy-gems-modal.sass +++ b/app/styles/play/modal/buy-gems-modal.sass @@ -1,10 +1,25 @@ +@import "app/styles/mixins" + #buy-gems-modal + //- Header + + h1 + position: absolute + left: 200px + top: -70px + margin: 0 + width: 612px + text-align: center + color: rgb(254,188,68) + font-size: 38px + text-shadow: black 4px 4px 0, black -4px -4px 0, black 4px -4px 0, black -4px 4px 0, black 4px 0px 0, black 0px -4px 0, black -4px 0px 0, black 0px 4px 0 + //- Clear modal defaults .modal-dialog - margin: 100px auto 0 auto + margin: 150px auto 0 auto padding: 0 - width: 820px + width: 1060px height: 460px background: none @@ -14,22 +29,40 @@ top: -157px left: -2px + //- Close modal button + + #close-modal + position: absolute + left: 770px + top: -80px + width: 60px + height: 60px + color: white + text-align: center + font-size: 30px + padding-top: 15px + cursor: pointer + @include rotate(-3deg) + + &:hover + color: yellow + //- Products #products position: absolute left: 55px top: 242px - width: 720px + width: 960px height: 140px - index: 1 .product width: 228px - overflow: none + height: 136px float: left text-align: center margin-right: 12px + position: relative h4 font-size: 20px @@ -41,9 +74,11 @@ color: rgb(22,16,5) button + position: absolute width: 80% - - + left: 10% + bottom: -30px + //- Errors .alert position: absolute diff --git a/app/styles/play/modal/item-details-view.sass b/app/styles/play/modal/item-details-view.sass index 6f3c661d4..2bc21598e 100644 --- a/app/styles/play/modal/item-details-view.sass +++ b/app/styles/play/modal/item-details-view.sass @@ -1,5 +1,18 @@ +@import "app/styles/bootstrap/variables" +@import "app/styles/mixins" + #item-details-view + .big-font + text-transform: uppercase + font-family: $headings-font-family + font-weight: bold + + .one-line + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + .nano-content padding: 10px @@ -42,6 +55,9 @@ .item-shadow top: 25px + left: 5px + @include filter(contrast(0%) brightness(0%)) + opacity: 0.2 img.hr width: 80% @@ -55,7 +71,7 @@ position: relative font-size: 20px font-weight: bold - + .stat-label position: absolute left: 54px @@ -66,6 +82,16 @@ left: 150px color: rgb(42,38,28) + &:not(.short-name) + text-align: center + + .stat-label, .stat + position: static + display: inline-block + + .stat-label + margin-right: 8px + .item-description margin: 15px 15px 0 25px @@ -90,4 +116,3 @@ .unequippable position: absolute - diff --git a/app/styles/play/modal/leaderboard-modal.sass b/app/styles/play/modal/leaderboard-modal.sass new file mode 100644 index 000000000..cd7dcdcd0 --- /dev/null +++ b/app/styles/play/modal/leaderboard-modal.sass @@ -0,0 +1,112 @@ +@import "app/styles/bootstrap/variables" +@import "app/styles/mixins" + +#leaderboard-modal + + //- Clear modal defaults + + .modal-dialog + width: 820px + height: 570px + padding: 0 + background: none + position: relative + top: 40px + + + //- Background + + #leaderboard-background + position: absolute + top: -126px + left: -3px + + + //- Header + + .level-title + position: absolute + left: 172px + top: -46px + margin: 0 + width: 457px + text-align: center + color: rgb(254,188,68) + font-size: 38px + text-shadow: black 4px 4px 0, black -4px -4px 0, black 4px -4px 0, black -4px 4px 0, black 4px 0px 0, black 0px -4px 0, black -4px 0px 0, black 0px 4px 0 + + + //- Close modal button + + #close-modal + position: absolute + left: 616px + top: -51px + width: 60px + height: 60px + color: white + text-align: center + font-size: 30px + padding-top: 17px + cursor: pointer + z-index: 2 + @include rotate(-3deg) + + &:hover + color: yellow + + + //- Nav bar + + #leaderboard-nav + position: absolute + top: 53px + left: 42px + width: 178px + + li + background: url(/images/pages/play/modal/menu-tab.png) + padding: 5px + margin: -5px 0 + height: 80px + padding: 0 + + &.active + background: url(/images/pages/play/modal/menu-tab-selected.png) + width: 197px + + a + font-size: 18px + line-height: 25px + background: none + color: rgb(195,153,124) + font-weight: bold + padding: 14px 20px + font-family: $headings-font-family + text-transform: uppercase + + .timespan + margin-left: 20px + opacity: 0.75 + + + //- Tab panels + + .leaderboard-tab-content + position: absolute + left: 219px + top: 55px + width: 571px + height: 490px + padding: 50px + overflow-y: scroll + + ::-webkit-scrollbar + // So that the scrollbar doesn't go on top of the close button. + // Wish we could easily do this for Firefox. + display: none + +// Font size tweak for languages that don't fit +body[lang='ru'] + #leaderboard-modal #leaderboard-nav a + font-size: 13px diff --git a/app/styles/play/modal/leaderboard-tab-view.sass b/app/styles/play/modal/leaderboard-tab-view.sass new file mode 100644 index 000000000..134324480 --- /dev/null +++ b/app/styles/play/modal/leaderboard-tab-view.sass @@ -0,0 +1,36 @@ +.leaderboard-tab-view + + h1 + margin-top: -40px + color: rgb(254,188,68) + font-size: 30px + text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0 + + table + + td + padding: 1px 2px + font-size: 16px + text-align: center + + th + text-align: center + + tbody + tr.viewable + cursor: pointer + + .rank-cell + font-weight: bold + + .name-col-cell + max-width: 150px + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + .hero-portrait-cell, .code-language-cell + background: transparent url(/images/common/code_languages/javascript_small.png) no-repeat center center + background-size: 30px 30px + height: 30px + width: 32px diff --git a/app/styles/play/modal/play-heroes-modal.sass b/app/styles/play/modal/play-heroes-modal.sass index 6b083718f..5aed4d59a 100644 --- a/app/styles/play/modal/play-heroes-modal.sass +++ b/app/styles/play/modal/play-heroes-modal.sass @@ -1,28 +1,29 @@ @import "app/styles/mixins" @import "app/styles/bootstrap/variables" -$heroCanvasHeight: 265px +$heroCanvasHeight: 275px #play-heroes-modal - + @include user-select(none) + //- Clear modal defaults - + .modal-dialog padding: 0 width: 820px height: 658px - - + + //- Background - + #play-heroes-background position: absolute top: -59px left: -20px - - + + //- Header - + h1 position: absolute left: 154px @@ -35,6 +36,19 @@ $heroCanvasHeight: 265px text-shadow: black 4px 4px 0, black -4px -4px 0, black 4px -4px 0, black -4px 4px 0, black 4px 0px 0, black 0px -4px 0, black -4px 0px 0, black 0px 4px 0 + //- Gems count + + #gems-count-container + position: absolute + right: 39px + top: 470px + + #gems-count + font-family: $headings-font-family + font-size: 24.5px + color: lighten(rgb(1,64,91), 50%) + + //- Close modal button #close-modal @@ -53,9 +67,9 @@ $heroCanvasHeight: 265px &:hover color: yellow - + //- Carousel character portraits - + #hero-carousel width: 750px height: 386px @@ -71,9 +85,10 @@ $heroCanvasHeight: 265px position: static width: 100% margin-left: 0 + white-space: nowrap .hero-indicator - width: 104px + width: 97px height: 98px margin: 0 -11px position: relative @@ -96,6 +111,9 @@ $heroCanvasHeight: 265px background-color: goldenrod @include filter(contrast(50%) brightness(65%)) + &.ie + @include opacity(0.35) + .lock-indicator position: absolute width: 40% @@ -114,27 +132,27 @@ $heroCanvasHeight: 265px //- Small transformations to jumble the hero icons a little - + .hero-index-0 transform: rotate(-5deg) z-index: 2 - + .hero-index-1 top: -3px z-index: 1 - + .hero-index-2 top: -3px transform: rotate(5deg) z-index: 1 - + .hero-index-3 transform: rotate(-1deg) z-index: 0 .hero-index-4 transform: rotate(3deg) - + .hero-index-5 z-index: 0 @@ -142,7 +160,7 @@ $heroCanvasHeight: 265px transform: rotate(6deg) top: -8px z-index: 1 - + .hero-index-8 transform: rotate(4deg) @@ -159,9 +177,9 @@ $heroCanvasHeight: 265px width: 334px height: $heroCanvasHeight float: left - + .hero-stats - width: 384px + width: 400px height: $heroCanvasHeight float: left @@ -174,27 +192,28 @@ $heroCanvasHeight: 265px .hero-stats color: white + @include user-select(text) h2 margin-top: 0px color: white - + .hero-description - margin-bottom: 10px - + margin-bottom: 4px + .hero-stat-row - margin: 5px 0 - + margin: 3px 0 + .stat-label float: left width: 100px color: rgb(203,170,148) - + .stat-value display: inline-block width: 280px color: rgb(244,189,68) - + .stat-progress background: rgb(32,27,22) height: 15px @@ -204,24 +223,24 @@ $heroCanvasHeight: 265px top: 2px left: -3px width: 70% - + .stat-progress-bar height: 7px border-radius: 7px - - + + &.attack .stat-progress-bar background: #c32424 - + &.health .stat-progress-bar background: #0f802a - + &.speed .stat-progress-bar background: #4d52ab - + //- Carousel switch buttons - + a.left, a.right color: rgb(74,61,51) position: absolute @@ -229,15 +248,15 @@ $heroCanvasHeight: 265px width: 40px height: 84px font-size: 24px - + .glyphicon position: relative top: 27px left: 8px - + &:hover, &:active color: rgb(126,105,88) - + a.right right: -49px @@ -248,7 +267,10 @@ $heroCanvasHeight: 265px //- Different footer states - #purchasable-hero-explanation + #hero-footer + @include user-select(text) + + #purchasable-hero-explanation, #loading-hero-explanation position: absolute left: 32px top: 532px @@ -274,7 +296,7 @@ $heroCanvasHeight: 265px margin-top: 12px margin-bottom: 5px - #purchase-hero-button + #restricted-hero-button, #purchase-hero-button width: 209px height: 110px position: absolute @@ -284,12 +306,14 @@ $heroCanvasHeight: 265px text-align: center text-transform: uppercase font-size: 24.5px - font-family: Open Sans Condensed + font-family: $headings-font-family color: white + + #purchase-hero-button border: 3px solid rgb(7,65,83) background: rgb(0,119,168) border-radius: 0 - + &:disabled background: rgb(72, 106, 113) opacity: 1 @@ -301,8 +325,10 @@ $heroCanvasHeight: 265px &:hover > * @include opacity(1) + //#restricted-hero-button + //- Programming select box - + .form position: absolute left: 32px @@ -310,33 +336,33 @@ $heroCanvasHeight: 265px width: 541px height: 102px padding: 10px 40px - + .help-block color: rgb(51,51,51) font-size: 14px font-weight: bold - + select font-size: 18px - + .fancy-select display: inline-block width: 100% .options text-transform: none - + .trigger, .options background-color: rgb(239,232,217) color: black - + .trigger text-transform: uppercase border: 3px solid black font-size: 16px padding: 5px 10px width: 100% - + //- the little triangle on the right side of the fancy select box &:after border: 8px solid transparent @@ -372,7 +398,7 @@ $heroCanvasHeight: 265px background-image: url(/images/common/code_languages/lua_small.png) &[data-value="io"] background-image: url(/images/common/code_languages/io_small.png) - + #confirm-button background: url(/images/pages/play/modal/confirm-button.png) width: 209px @@ -384,11 +410,19 @@ $heroCanvasHeight: 265px text-align: center text-transform: uppercase font-size: 26px - font-family: Open Sans Condensed + font-family: $headings-font-family color: white - + body.ipad #play-heroes-modal // iPad is Python-only for now, and has its own reset button. .form display: none + +body[lang='ru'] + #play-heroes-modal + #hero-carousel .hero-item .hero-stats h2 + font-size: 24px + + #purchase-hero-button + font-size: 18px diff --git a/app/styles/play/modal/play-items-modal.sass b/app/styles/play/modal/play-items-modal.sass index ab7a3c5ca..8a214b203 100644 --- a/app/styles/play/modal/play-items-modal.sass +++ b/app/styles/play/modal/play-items-modal.sass @@ -1,25 +1,25 @@ +@import "app/styles/bootstrap/variables" @import "app/styles/mixins" #play-items-modal - + .big-font text-transform: uppercase - font-family: "Open Sans Condensed" + font-family: $headings-font-family font-weight: bold - + .one-line white-space: nowrap overflow: hidden text-overflow: ellipsis - //- Clear modal defaults .modal-dialog padding: 0 width: 1230px height: 660px background: none - + //- Background #play-items-modal-bg, #play-items-modal-narrow-bg position: absolute @@ -28,9 +28,8 @@ #play-items-modal-narrow-bg display: none - + //- Header - h1 position: absolute left: 200px @@ -39,10 +38,8 @@ font-size: 38px text-shadow: black 4px 4px 0, black -4px -4px 0, black 4px -4px 0, black -4px 4px 0, black 4px 0px 0, black 0px -4px 0, black -4px 0px 0, black 0px 4px 0 margin: 0 - - - //- Gems count + //- Gems count #gems-count-container position: absolute left: 425px @@ -50,17 +47,15 @@ width: 160px height: 66px @include rotate(5deg) - + #gems-count position: absolute left: 75px top: 17px font-size: 25px color: rgb(1,64,91) - - + //- Close modal button - #close-modal position: absolute left: 602px @@ -69,30 +64,28 @@ height: 60px color: white text-align: center - font-size: 30px + font-size: 30px padding-top: 7px cursor: pointer @include rotate(-3deg) &:hover color: yellow - - + //- Nav bar - .nav position: absolute top: 125px left: -31px width: 178px - + li background: url(/images/pages/play/modal/menu-tab.png) padding: 5px margin: -5px 0 height: 80px padding: 0 - + a font-size: 18px line-height: 50px @@ -100,18 +93,36 @@ color: rgb(195,153,124) font-weight: bold padding: 10px 7px - - + li.active background: url(/images/pages/play/modal/menu-tab-selected.png) width: 197px - + a color: white - - - //- Item List + //- Hero Type Select + #hero-type-select + position: absolute + top: 102px + left: 177px + background: rgb(26,21,17) + padding: 2px 0 + z-index: 3 + border-radius: 2px + + label + background: rgb(58,47,38) + color: rgb(195,153,124) + border: 2px solid rgb(85,70,57) + margin: 0 2px + + &.active + background: rgb(33,28,21) + border: 2px solid rgb(64,53,41) + color: white + + //- Item List .tab-content position: absolute top: 116px @@ -120,15 +131,25 @@ height: 507px overflow: hidden + &.filter-warrior + .item.Ranger, .item.Wizard + display: none + + &.filter-ranger + .item.Warrior, .item.Wizard + display: none + + &.filter-wizard + .item.Ranger, .item.Warrior + display: none + .tab-pane height: 100% - + .nano-content padding: 26px 51px 26px 26px - //- Item box - .item cursor: pointer width: 187px @@ -142,7 +163,7 @@ &.silhouetted cursor: default - + strong position: absolute top: 7px @@ -165,7 +186,7 @@ top: 25px width: 110px height: 110px - + .glyphicon-lock font-size: 60px position: absolute @@ -176,11 +197,11 @@ right: 0 margin-left: auto margin-right: auto - + &.bolder font-weight: bolder color: rgb(211,200,175) - + .unlock-button, .unequippable right: 1px bottom: 0 @@ -192,7 +213,7 @@ position: absolute line-height: 41px display: inline-block - + .cost position: absolute height: 41px @@ -203,14 +224,14 @@ font-size: 16px color: rgb(22,61,73) font-weight: bold - + img width: 22px height: 22px margin-right: 8px position: relative top: -2px - + .owned, .locked position: absolute left: 0 @@ -224,41 +245,35 @@ &.selected background: url(/images/pages/play/modal/item-box-background-selected.png) - //- Item details. Non-specific item-details-view styling is in item-details-view.sass. - #item-details-view - + #item-title left: 910px top: 60px - + #item-details-body left: 860px - + #selected-item-unlock-button left: 856px - #play-items-modal, #inventory-modal //- Item list scrollbar - .nano-pane width: 16px background: black border: 3px solid rgb(97,76,58) - + .nano-slider background: rgb(244,170,66) border: 3px solid black border-radius: 10px margin-left: -3px margin-right: -3px - //- Item icons w/shadows (both in list and details areas) - .item-img, .item-shadow, .item-silhouette position: absolute margin-left: auto @@ -270,14 +285,15 @@ .item-img z-index: 1 - .item-shadow - left: 5px - @include filter(contrast(0%) brightness(0%)) - opacity: 0.2 - + // Not performant, takes too much memory with filter. + //.item-shadow + // left: 5px + // @include filter(contrast(0%) brightness(0%)) + // opacity: 0.2 + .item-silhouette - @include filter(contrast(0%) brightness(0%)) - opacity: 0.3 + opacity: 0.2 + //@include filter(contrast(0%) brightness(0%)) .required-level position: absolute @@ -286,14 +302,12 @@ top: 70px font-size: 20px line-height: 20px - font-family: Open Sans Condensed + font-family: $headings-font-family text-transform: uppercase font-weight: bold z-index: 2 - //- Unlock buttons (both in list and details areas) - .unlock-button position: absolute border: 3px solid rgb(7,65,83) @@ -301,7 +315,7 @@ color: white font-size: 16px border-radius: 0 - + &:disabled background: rgb(72, 106, 113) opacity: 1 @@ -316,7 +330,6 @@ margin-top: 20px //- Use the two-column layout and background image if we are on a narrow screen. - @media only screen and (max-width: 1300px) #play-items-modal overflow-x: hidden @@ -347,12 +360,20 @@ padding-left: 20px #item-details-view - + #item-title left: 698px - + #item-details-body - left: 648px - + left: 648px + #selected-item-unlock-button, .unequippable left: 645px + +body[lang='pt-PT'], body[lang^='pt-BR'] + #play-items-modal .unlock-button + font-size: 13px + +body[lang='ru'] + #play-items-modal .unlock-button + font-size: 10px diff --git a/app/styles/play/modal/poll-modal.sass b/app/styles/play/modal/poll-modal.sass new file mode 100644 index 000000000..da72c99be --- /dev/null +++ b/app/styles/play/modal/poll-modal.sass @@ -0,0 +1,192 @@ +@import "app/styles/bootstrap/variables" +@import "app/styles/mixins" + +#poll-modal + $hero-yellow-text: rgb(252, 201, 53) + + //- Top-level modal container + .modal-dialog + margin-top: 0 + padding-top: 0 + width: 750px + + .modal-content + position: relative + margin-top: -251px + + @media only screen and (max-height: 720px) + .modal-dialog + margin-top: -76px + + //- Header + + .background-wrapper + //background: url("/images/pages/play/level/modal/victory_modal_background.png") + width: 750px + background: transparent + border: 0px solid transparent + border-style: solid + border-image: url("/images/pages/play/level/modal/victory_modal_border_background.png") 250 0 100 0 fill round + border-width: 250px 0 100px 0 + border-radius: 12px + + .modal-header + border: none + position: absolute + left: 188px + width: 378px + height: 134px + margin: 0 + padding: 0 + + .close + display: none + + h1 + position: absolute + left: 0 + top: 90px + margin: 0 + width: 380px + text-align: center + color: rgb(254,188,68) + font-size: 28px + text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0 + + //- Body + + .modal-body + padding: 0 20px + min-height: 30px + margin-top: 160px + + .description + margin: 20px 5px 0 5px + color: white + text-align: center + + .answers-container-wrapper + margin-top: 5px + border: 4px solid rgb(26, 21, 17) + + .answers-container + background-color: rgb(45, 36, 29) + border: 4px solid rgb(74, 61, 48) + padding: 15px + + table.table + margin-bottom: 0 + + tr.answer + &:not(.selected) td + background-color: rgb(74, 61, 48) + border-color: lighten(rgb(74, 61, 48), 10%) + + &:hover:not(.selected) td + background-color: lighten(rgb(74, 61, 48), 10%) + + &.selected td + background-color: rgb(33, 28, 21) + border-color: lighten(rgb(33, 28, 21), 10%) + + &:not(.selected) + cursor: pointer + + td + vertical-align: middle + + code + padding: 2px 4px + font-size: 90% + color: white + background-color: #333 + border-radius: 3px + @include box-shadow(inset 0 -1px 0 rgba(0, 0, 0, .25)) + + &.graph-cell + min-width: 200px + + p + margin: 0 + color: white + + .progress + width: 100% + margin-bottom: 0 + background-color: rgb(45, 36, 29) + border-radius: 10px + + .progress-bar + background-color: rgb(245, 170, 49) + border-radius: 10px + border: 3px solid rgb(45, 36, 29) + @include transition(none) + + &.votes-cell + max-width: 34px + + .badge + background-color: rgb(245, 170, 49) + color: rgb(45, 36, 29) + + table:not(.answered) + tr + text-align: center + + .graph-cell, .votes-cell + display: none + + .random-gems-container-wrapper + width: 558px + height: 115px + background: transparent url(/images/pages/play/modal/random-gems-background.png) no-repeat 100% 100% + padding: 25px + margin: 10px auto + + &:not(.answered) + display: none + + .random-gems-container + + .random-gems-code + font-size: 14px + display: block + white-space: pre + padding: 2px 4px + font-size: 90% + color: black + background-color: transparent + border-radius: 0 + margin-bottom: 5px + + .comment + font-weight: bold + color: darken(rgb(245, 170, 49), 30%) + + //- Footer - other stuff + + .modal-footer + // Negative bottom margin counteracts most of the extra the border image height. + margin: 0 0 -80px 0 + padding: 0 20px + text-align: center + + .done-button + float: right + height: 60px + min-width: 100px + line-height: 30px + margin: 0 10px + +html.no-borderimage + #poll-modal + .modal-dialog + margin-top: 251px + .background-wrapper + border: 0 + background: url("/images/pages/play/level/modal/victory_modal_background.png") + height: 713px + + @media only screen and (max-height: 720px) + .modal-dialog + margin-top: 175px diff --git a/app/styles/play/modal/share-progress-modal.sass b/app/styles/play/modal/share-progress-modal.sass new file mode 100644 index 000000000..6e2eb771f --- /dev/null +++ b/app/styles/play/modal/share-progress-modal.sass @@ -0,0 +1,81 @@ +@import "app/styles/mixins" +@import "app/styles/bootstrap/variables" + +#share-progress-modal + + .modal-dialog + margin: 60px auto 0 auto + padding: 0 + height: 460px + width: 700px + background: none + + .modal-content + height: 100% + width: 100% + + .background-img + position: absolute + top: -61px + left: 0px + height: 100% + width: 100% + + .wizard-img + position: absolute + top: 70px + left: 32px + height: 301px + + .blurb-container + position: absolute + right: 50px + top: 70px + margin: 0 + width: 300px + + h1 + font-size: 28px + font-weight: bold + color: black + + .send-container + margin-top: 10px + .email-form + .email-input + width: 200px + + button + color: white + width: 80px + font-size: 28px + line-height: 28px + text-transform: none + font-variant: small-caps + font-family: "Open Sans Condensed", "Helvetica Neue", Helvetica, Arial, sans-serif + button.back-btn + border-image: url(/images/common/button-background-primary-disabled.png) 14 20 20 20 fill round + button.send-btn + border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round + + .continue-container + margin-top: 10px + margin-right: -12px + .continue-link + color: black + font-weight: normal + font-size: 11px + text-decoration: underline + + .email-invalid + color: red + display: none + + //- Errors + + .alert + position: absolute + left: 10% + width: 80% + top: 20px + border: 5px solid gray diff --git a/app/styles/play/spectate.sass b/app/styles/play/spectate.sass index d102a7197..74b94a609 100644 --- a/app/styles/play/spectate.sass +++ b/app/styles/play/spectate.sass @@ -25,15 +25,10 @@ width: 60% text-align: center - max-width: 1920px margin: 0 auto @include user-select(none) - #level-loading-view - max-height: 1284px - .level-content - //max-width: 1920px position: relative margin: 0px auto @@ -104,13 +99,6 @@ &.btn-#{nth($tuple, 1)} @include banner-button(nth($tuple, 2), #FFF) - .footer .footer-link-text a - @include opacity(0.75) - @include transition(opacity .10s linear) - - &:hover, &:active - @include opacity(1) - $GI: 0.5 // gradient intensity; can tweak this 0-1 .gradient @@ -152,22 +140,3 @@ top: 0 height: 100% width: 2% - - .footer - @media screen and (min-aspect-ratio: 17/10) - display: none - - &:not(:hover) - @include opacity(0.6) - - .hour-of-code-explanation - margin-top: 5px - color: white - font-size: 12px - - &:not(:hover) - @include opacity(0.75) - - a - color: white - text-decoration: underline diff --git a/app/styles/play/world-map-view.sass b/app/styles/play/world-map-view.sass deleted file mode 100644 index 86658769a..000000000 --- a/app/styles/play/world-map-view.sass +++ /dev/null @@ -1,318 +0,0 @@ -@import "app/styles/mixins" -@import "app/styles/bootstrap/variables" - -$mapHeight: 1536 -$forestMapWidth: 2500 -$dungeonMapWidth: 2350 -$forestMapSeaBackground: #71bad0 -$dungeonMapCaveBackground: rgba(68, 54, 45, 1) -$dungeonMapCaveBackgroundTransparent: rgba(68, 54, 45, 0) -$levelDotWidth: 2% -$levelDotHeight: $levelDotWidth * $forestMapWidth / $mapHeight -$levelDotZ: $levelDotHeight * 0.25 -$levelDotHoverZ: $levelDotZ * 2 -$levelDotShadowWidth: 0.8 * $levelDotWidth -$levelDotShadowHeight: 0.8 * $levelDotHeight -$levelClickRadius: 40px -$gameControlSize: 80px -$gameControlMargin: 30px - -+keyframes(levelStartedPulse) - from - @include box-shadow(0px 0px 4px #333) - margin-bottom: -$levelDotHeight / 3 + $levelDotZ - 50% - @include box-shadow(0px 0px 22px skyblue) - margin-bottom: -$levelDotHeight / 3 + ($levelDotHoverZ + $levelDotZ) / 2 - to - @include box-shadow(0px 0px 4px #333) - margin-bottom: -$levelDotHeight / 3 + $levelDotZ - -#world-map-view - width: 100% - height: 100% - position: absolute - - &.forest - background-color: $forestMapSeaBackground - - &.dungeon - background-color: $dungeonMapCaveBackground - - .gradient - position: absolute - z-index: 0 - - &.horizontal-gradient - left: 0 - right: 0 - height: 3% - - &.vertical-gradient - top: 0 - bottom: 0 - width: 3% - - &.top-gradient - top: 0 - background: linear-gradient(to bottom, $dungeonMapCaveBackground 0%, $dungeonMapCaveBackgroundTransparent 100%) - - &.right-gradient - right: 0 - background: linear-gradient(to left, $dungeonMapCaveBackground 0%, $dungeonMapCaveBackgroundTransparent 100%) - - &.bottom-gradient - bottom: 0 - background: linear-gradient(to top, $dungeonMapCaveBackground 0%, $dungeonMapCaveBackgroundTransparent 100%) - - &.left-gradient - left: 0 - background: linear-gradient(to right, $dungeonMapCaveBackground 0%, $dungeonMapCaveBackgroundTransparent 100%) - - .map - position: relative - - .map-background - width: 100% - height: 100% - - .level, .level-shadow - position: absolute - border-radius: 100% - -webkit-transform: scaleY(0.75) - transform: scaleY(0.75) - - .level - z-index: 2 - width: $levelDotWidth - height: $levelDotHeight - margin-left: -0.5 * $levelDotWidth - margin-bottom: -$levelDotHeight / 3 + $levelDotZ - border: 2px groove white - @include transition(margin-bottom 0.5s ease) - - &.disabled, &.locked - background-image: url(/images/pages/game-menu/lock.png) - background-size: 75% - background-repeat: no-repeat - background-position: 50% 50% - opacity: 0.7 - - a - cursor: default - - &.next - width: 2 * $levelDotWidth - height: 2 * $levelDotHeight - margin-left: -0.5 * 2 * $levelDotWidth - margin-bottom: -2 * $levelDotHeight / 3 + 2 * $levelDotZ - - &.started, &.next - border: 3px solid lightgreen - @include box-shadow(0px 0px 35px skyblue) - - // Would be cool, but kills performance, since we have to re-render all the time. - //&:not(:hover) - // -webkit-animation-name: levelStartedPulse - // -webkit-animation-duration: 3s - // -webkit-animation-iteration-count: infinite - - &.complete - border: 3px solid gold - @include box-shadow(0px 0px 35px skyblue) - - .level-shadow - z-index: 1 - width: $levelDotShadowWidth - height: $levelDotShadowHeight - margin-left: -0.5 * $levelDotShadowWidth - margin-bottom: -$levelDotShadowHeight / 3 - background-color: black - @include box-shadow(0px 0px 10px black) - @include opacity(0.75) - - &.next - width: 2 * $levelDotShadowWidth - height: 2 * $levelDotShadowHeight - margin-left: -0.5 * 2 * $levelDotShadowWidth - margin-bottom: -2 * $levelDotShadowHeight / 3 - - .level:hover - margin-bottom: -$levelDotHeight / 3 + $levelDotHoverZ - @include box-shadow(0px 0px 35px skyblue) - - &.next - margin-bottom: -2 * $levelDotHeight / 3 + 2 * $levelDotHoverZ - - .level - a - display: block - padding: $levelClickRadius - margin-left: -0.5 * $levelClickRadius - margin-top: -0.5 * $levelClickRadius - border-radius: $levelClickRadius - - &.next a - padding: 2 * $levelClickRadius - margin-left: 2 * -0.5 * $levelClickRadius - margin-top: 2 * -0.5 * $levelClickRadius - border-radius: 2 * $levelClickRadius - - .level-info-container - display: none - position: absolute - z-index: 3 - padding: 10px - border-width: 15px - // Using modernizr-mixin for compat detection - @include yep(borderimage) - border-image: url(/images/level/popover_background.png) 18 fill round - @include nope(borderimage) - background-color: rgb(247, 242, 218) - - .level-info.complete h3:after - content: " - Complete!" - color: green - - .level-info.started h3:after - content: " - Started" - color: desaturate(green, 50%) - - .level-info - h3 - margin-top: 0 - margin-bottom: 0px - - .level-description - color: black - text-shadow: 0 1px 0 white - - .campaign-label - text-shadow: 0 1px 0 white - - .start-level - display: block - margin: 10px auto 0 auto - width: 200px - - .campaign-switch - color: purple - position: absolute - z-index: 1 - font-size: 2vw - text-shadow: 0 0 0.3vw white, 0 0 0.3vw white - - &:hover - text-decoration: none - - &#forest-link - left: 94.5% - top: 7% - transform: rotate(-35deg) - - &#dungeon-link - left: 26.6% - top: 43% - transform: rotate(180deg) - - .game-controls - position: absolute - right: 1% - bottom: 1% - z-index: 3 - - .btn - &:not(:first-child) - margin-left: $gameControlMargin - width: $gameControlSize - height: $gameControlSize - - background: url(/images/pages/play/menu_icons.png) no-repeat - - position: relative - img - position: absolute - left: 0 - top: 0 - width: 100% - height: 100% - - background-size: cover - @include transition(0.5s ease) - @include box-shadow(2px 2px 4px black) - border: 0 - border-radius: 12px - // IE9 shows a blank white button with this MS gradient filter in place - filter: none - - &:hover - @include box-shadow(0 0 12px #bbf) - - &:active - @include box-shadow(0 0 20px white) - - &.heroes - background-position: (-1 * $gameControlSize) 0px - &.achievements - background-position: (-2 * $gameControlSize) 0px - &.account - background-position: (-3 * $gameControlSize) 0px - &.settings - background-position: (-4 * $gameControlSize) 0px - &.gems - background-position: (-5 * $gameControlSize) 0px - - .tooltip - font-size: 24px - - .user-status - position: absolute - bottom: 1% - left: 1% - text-align: center - font-size: 24px - color: white - text-shadow: 1px 1px 0px black - - button - margin-left: 10px - - #volume-button - position: absolute - left: 1% - top: 1% - padding: 3px 8px - @include opacity(0.75) - - &:hover - @include opacity(1.0) - - .glyphicon - display: none - font-size: 32px - - &.vol-up .glyphicon.glyphicon-volume-up - display: inline-block - - &.vol-off .glyphicon.glyphicon-volume-off - display: inline-block - @include opacity(0.50) - &:hover - @include opacity(0.75) - - &.vol-down .glyphicon.glyphicon-volume-down - display: inline-block - -body:not(.ipad) #world-map-view - .level-info-container - pointer-events: none - - - -body.ipad #world-map-view - // iPad only supports up to Kithgard Gates for now. - .campaign-switch - display: none - - .old-levels - display: none diff --git a/app/styles/teachers-free-trial.sass b/app/styles/teachers-free-trial.sass new file mode 100644 index 000000000..feb62ea96 --- /dev/null +++ b/app/styles/teachers-free-trial.sass @@ -0,0 +1,20 @@ +#teachers-free-trial-view + + .input-email-address + width: 40% + + .input-school + width: 40% + + .input-location + width: 40% + + .input-heard-about + width: 100% + + .thanks-submit + display: none + + .error-message + display: none + color: red diff --git a/app/styles/teachers.sass b/app/styles/teachers.sass index 7338dcd5c..9690ced02 100644 --- a/app/styles/teachers.sass +++ b/app/styles/teachers.sass @@ -1,5 +1,14 @@ #teachers-view - .main-content-area - width: 650px - box-shadow: 0px 0px 0px \ No newline at end of file + .main-content-area + width: 650px + box-shadow: 0px 0px 0px + + table + background-color: #F9F1DD + + .discount-table + width: 50% + + .teachers-title + color: green diff --git a/app/templates/about.jade b/app/templates/about.jade index a300bcd70..e156b4e9e 100644 --- a/app/templates/about.jade +++ b/app/templates/about.jade @@ -47,7 +47,17 @@ block content a(href="https://s3.amazonaws.com/CodeCombatMisc/press_packet.zip", data-i18n="about.press_paragraph_1_link") press packet span(data-i18n="about.press_paragraph_1_suffix") | . All logos and images may be used without contacting us directly. - + + h2(data-i18n="nav.contact") + | Contact + p + span CodeCombat, Inc. + br + span 188 King St #507 + br + span San Francisco, CA 94107 + br + a(href='mailto:team@codecombat.com') team@codecombat.com ul.col-sm-6.team-column @@ -57,70 +67,108 @@ block content h2(data-i18n="about.team") Team - img(src="/images/pages/about/george_small.png").img-thumbnail - - .team_bio - - h4.team_name George Saines - - p(data-i18n="about.george_title") - | CEO - p(data-i18n="about.george_blurb") - | Businesser - - a(href="http://scotterickson.info") - img(src="/images/pages/about/scott_small.png").img-thumbnail - - .team_bio - - h4.team_name - a(href="http://scotterickson.info") Scott Erickson - - p(data-i18n="about.scott_title") - | Programmer - p(data-i18n="about.scott_blurb") - | Reasonable one - - li.row - a(href="http://www.nickwinter.net") img(src="/images/pages/about/nick_small.png").img-thumbnail - .team_bio - h4.team_name a(href="http://www.nickwinter.net") Nick Winter - p(data-i18n="about.nick_title") - | Programmer + | Cofounder p(data-i18n="about.nick_blurb") | Motivation Guru + a(href="http://www.mattlott.com/") + img(src="/images/pages/about/matt_small.png").img-thumbnail + .team_bio + h4.team_name + a(href="http://www.mattlott.com/") Matt Lott + p(data-i18n="about.matt_title") + | Cofounder + p(data-i18n="about.matt_blurb") + | Bicyclist + + li.row + + a(href="http://www.georgesaines.com/") + img(src="/images/pages/about/george_small.png").img-thumbnail + .team_bio + h4.team_name + a(href="http://www.georgesaines.com/") George Saines + p(data-i18n="about.george_title") + | Cofounder + p(data-i18n="about.george_blurb") + | Businesser + + a(href="http://cat.zdh.com/") + img(src="/images/pages/about/cat_small.png").img-thumbnail + .team_bio + h4.team_name + a(href="http://cat.zdh.com/") Catherine Weresow + p(data-i18n="about.cat_title") + | Chief Artisan + p(data-i18n="about.cat_blurb") + | Airbender + + li.row + + img(src="/images/pages/about/scott_small.png").img-thumbnail + .team_bio + h4.team_name Scott Erickson + p(data-i18n="about.scott_title") + | Cofounder + p(data-i18n="about.scott_blurb") + | Reasonable one + a(href="http://michaelschmatz.com") img(src="/images/pages/about/michael_small.png").img-thumbnail - .team_bio - h4.team_name a(href="http://michaelschmatz.com/") Michael Schmatz - p(data-i18n="about.michael_title") | Programmer p(data-i18n="about.michael_blurb") | Sys Admin + + li.row + + a(href="http://floor.is/lava/") + img(src="/images/pages/about/josh_small.png").img-thumbnail + .team_bio + h4.team_name + a(href="http://floor.is/lava/") Josh Lee + p(data-i18n="about.josh_title") + | Game Designer + p(data-i18n="about.josh_blurb") + | Floor Is Lava + + a(href="https://soundcloud.com/taking-off") + img(src="/images/pages/about/jose_small.png").img-thumbnail + .team_bio + h4.team_name + a(href="https://soundcloud.com/taking-off") Jose Antonini + p(data-i18n="about.jose_title") + | Music + p(data-i18n="about.jose_blurb") + | Taking Off li.row - img(src="/images/pages/about/matt_small.png").img-thumbnail - + a(href="http://retrostylegames.com/") + img(src="/images/pages/about/pavel_small.png").img-thumbnail .team_bio - h4.team_name - a(href="http://www.mattlott.com/") Matt Lott - - p(data-i18n="about.matt_title") - | Programmer - p(data-i18n="about.matt_blurb") - | Bicyclist - + a(href="http://retrostylegames.com/") Pavel Konstantinov + p(data-i18n="about.retrostyle_title") + | Illustration + p(data-i18n="about.retrostyle_blurb") + | RetroStyle Games + a(href="http://retrostylegames.com/") + img(src="/images/pages/about/oleg_small.png").img-thumbnail + .team_bio + h4.team_name + a(href="http://retrostylegames.com/") Oleg Ulyanickiy + p(data-i18n="about.retrostyle_title") + | Illustration + p(data-i18n="about.retrostyle_blurb") + | RetroStyle Games diff --git a/app/templates/account/account-settings-root-view.jade b/app/templates/account/account-settings-root-view.jade index 89f07eca5..feae7db6b 100644 --- a/app/templates/account/account-settings-root-view.jade +++ b/app/templates/account/account-settings-root-view.jade @@ -12,8 +12,8 @@ block content if !me.get('anonymous', true) #save-button-container - button#save-button.btn-lg.btn.disabled(data-i18n="general.save" disabled="true") No Changes + button#save-button.btn-lg.btn.disabled(data-i18n="delta.no_changes" disabled="true") No Changes #account-settings-view -block footer \ No newline at end of file +block footer diff --git a/app/templates/account/account-settings-view.jade b/app/templates/account/account-settings-view.jade index 00db82515..ca871ef2a 100644 --- a/app/templates/account/account-settings-view.jade +++ b/app/templates/account/account-settings-view.jade @@ -12,6 +12,7 @@ else - var name = me.get('name') || ''; - var email = me.get('email'); - var admin = me.get('permissions', true).indexOf('admin') != -1; + - var godmode = me.get('permissions', true).indexOf('godmode') != -1; .form-group label.control-label(for="name", data-i18n="general.name") Name input#name.form-control(name="name", type="text", value="#{name}") @@ -22,7 +23,10 @@ else .form-group.checkbox label(for="admin", data-i18n="account_settings.admin") Admin input#admin(name="admin", type="checkbox", checked=admin) - + .form-group.checkbox + label(for="godmode", data-i18n="account_settings.god_mode") God Mode + input#godmode(name="godmode", type="checkbox", checked=godmode) + .panel.panel-default .panel-heading @@ -44,6 +48,19 @@ else label.control-label(for="password2", data-i18n="account_settings.new_password_verify") Verify input#password2.form-control(name="password2", type="password") + .panel.panel-default + .panel-heading + .panel-title#delete-account-panel-title(data-i18n="account_settings.delete_account_tab") + .panel-body + .form + .form-group + label.control-label(for="email1", data-i18n="account_settings.type_in_email") + input#email1.form-control(name="email1", type="text") + .form-group + label.control-label(for="password1", data-i18n="account_settings.type_in_password") + input#password1.form-control(name="password1", type="password") + button#delete-account-button.btn.form-control.btn-primary(data-i18n="account_settings.delete_this_account") + .col-md-6 #email-panel.panel.panel-default diff --git a/app/templates/account/invoices-view.jade b/app/templates/account/invoices-view.jade new file mode 100644 index 000000000..d90965ab5 --- /dev/null +++ b/app/templates/account/invoices-view.jade @@ -0,0 +1,52 @@ +extends /templates/base + +block content + + if me.get('anonymous') + p(data-i18n="account_invoices.not_logged_in") + else + ol.breadcrumb + li + a(href="/") + span.glyphicon.glyphicon-home + li + a(href="/account", data-i18n="nav.account") + li.active(data-i18n="account.invoices") + + if state === 'purchasing' + .alert.alert-info(data-i18n="account_invoices.purchasing") + else if state === 'retrying' + #retrying-alert.alert.alert-danger(data-i18n="account_invoices.retrying") + else + .form + .form-group + label.control-label(for="amount", data-i18n="account_invoices.amount") + input#amount.form-control(name="amount", type="text", value="#{amount}") + .form-group + label.control-label(for="description", data-i18n="general.description") + input#description.form-control(name="description", type="text", value="#{description}") + button#pay-button.btn.form-control.btn-primary(data-i18n="account_invoices.pay") + + br + + if state === 'invoice_paid' + #declined-alert.alert.alert-success.alert-dismissible + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + p= stateMessage + if state === 'validation_error' + #declined-alert.alert.alert-danger.alert-dismissible + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + p= stateMessage + if state === 'declined' + #declined-alert.alert.alert-danger.alert-dismissible + span(data-i18n="account_invoices.declined") + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + if state === 'unknown_error' + #error-alert.alert.alert-danger.alert-dismissible + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + p(data-i18n="loading_error.unknown") + p= stateMessage diff --git a/app/templates/account/job_profile_code_modal.jade b/app/templates/account/job-profile-code-modal.jade similarity index 87% rename from app/templates/account/job_profile_code_modal.jade rename to app/templates/account/job-profile-code-modal.jade index 6f5703ef6..52cabcf51 100644 --- a/app/templates/account/job_profile_code_modal.jade +++ b/app/templates/account/job-profile-code-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3 Applicant Code for diff --git a/app/templates/account/job_profile.jade b/app/templates/account/job-profile-treema-view.jade similarity index 100% rename from app/templates/account/job_profile.jade rename to app/templates/account/job-profile-treema-view.jade diff --git a/app/templates/account/profile.jade b/app/templates/account/job-profile-view.jade similarity index 99% rename from app/templates/account/profile.jade rename to app/templates/account/job-profile-view.jade index 404de1521..f0dae7d07 100644 --- a/app/templates/account/profile.jade +++ b/app/templates/account/job-profile-view.jade @@ -5,7 +5,7 @@ block content div(class="job-profile-container") h1#login-message |Please - a.auth-button login + a.login-button login | to view this profile. else if user.loaded if allowedToEditJobProfile diff --git a/app/templates/account/main-account-view.jade b/app/templates/account/main-account-view.jade index f7c56817b..b41c5016a 100644 --- a/app/templates/account/main-account-view.jade +++ b/app/templates/account/main-account-view.jade @@ -15,7 +15,12 @@ block content #account-links.panel.panel-default .panel-heading(data-i18n="nav.account") ul.list-group + li.list-group-item + a.btn.btn-lg.btn-primary(href="/user/#{me.getSlugOrID()}", data-i18n="nav.profile") li.list-group-item a.btn.btn-lg.btn-primary(href="/account/settings", data-i18n="play.settings") li.list-group-item - a.btn.btn-lg.btn-primary(href="/account/payments", data-i18n="account.payments") \ No newline at end of file + a.btn.btn-lg.btn-primary(href="/account/payments", data-i18n="account.payments") + li.list-group-item + a.btn.btn-lg.btn-primary(href="/account/subscription", data-i18n="account.subscription") + diff --git a/app/templates/account/payments-view.jade b/app/templates/account/payments-view.jade index 3bcc40ee6..f3338e862 100644 --- a/app/templates/account/payments-view.jade +++ b/app/templates/account/payments-view.jade @@ -13,6 +13,7 @@ block content if payments.models.length table.table.table-striped tr + th(data-i18n="account.purchased") th(data-i18n="account.paid_on") th(data-i18n="account.service") th(data-i18n="account.price") @@ -20,7 +21,11 @@ block content for payment in payments.models - var service = payment.get('service') tr - td= moment(payment.getCreationDate()).format('lll') + if payment.get('productID') + td(data-i18n='account.gems') + else + td(data-i18n='subscribe.stripe_description') + td= moment(payment.getCreationDate()).format('l') if service === 'ios' td(data-i18n="account.service_apple") td= payment.get('ios').localPrice diff --git a/app/templates/account/subscription-view.jade b/app/templates/account/subscription-view.jade new file mode 100644 index 000000000..025b65256 --- /dev/null +++ b/app/templates/account/subscription-view.jade @@ -0,0 +1,196 @@ +extends /templates/base + +block content + + ol.breadcrumb + li + a(href="/") + span.glyphicon.glyphicon-home + li + a(href="/account", data-i18n="nav.account") + li.active(data-i18n="account.subscription") + + if me.get('anonymous') + p(data-i18n="account_settings.not_logged_in") + else + + //- Personal Subscriptions + + .panel.panel-default + .panel-heading + h3(data-i18n="subscribe.personal_sub") + if personalSub.prepaidCode && !personalSub.usingPrepaidCode + div + span(data-i18n="subscribe.subscribe_prepaid") + span.spl.spr= personalSub.prepaidCode + .panel-body + if personalSub.state === 'loading' + .alert.alert-info(data-i18n="subscribe.loading_info") + else if personalSub.state === 'subscribing' + .alert.alert-info(data-i18n="subscribe.subscribing") + else if personalSub.sponsor + div + span(data-i18n="subscribe.managed_by") + span.spl.spr #{personalSub.sponsorName} (#{personalSub.sponsorEmail}) + if personalSub.endDate + div + span(data-i18n="subscribe.will_be_cancelled") + span.spl.spr= moment(personalSub.endDate).format('l') + + else if personalSub.usingPrepaidCode + div(data-i18n="subscribe.using_prepaid") + + else if personalSub.self + if personalSub.state === 'declined' + .alert.alert-danger.alert-dismissible + span(data-i18n="buy_gems.declined") + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + br + else if personalSub.state === 'unknown_error' + .alert.alert-danger.alert-dismissible + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + p(data-i18n="loading_error.unknown") + p= personalSub.stateMessage + br + + if !personalSub.subscribed || personalSub.prepaidCode + button.start-subscription-button.btn.btn-lg.btn-success(data-i18n="subscribe.subscribe_title") Subscribe + else + button.end-subscription-button.btn.btn-lg.btn-warning(data-i18n="subscribe.unsubscribe") Unsubscribe + + .unsubscribe-feedback.row.secret + .col-lg-7 + h3 + if personalSub.monthsSubscribed > 1 + span(data-i18n="subscribe.thank_you_months_prefix") Thank you for supporting us these last + span.spl.spr= personalSub.monthsSubscribed + span(data-i18n="subscribe.thank_you_months_suffix") months. + else + span(data-i18n="subscribe.thank_you") Thank you for supporting CodeCombat. + div(data-i18n="subscribe.sorry_to_see_you_go") Sorry to see you go! Please let us know what we could have done better. + textarea(rows=3, data-i18n="[placeholder]subscribe.unsubscribe_feedback_placeholder") + .col-lg-5 + button.cancel-end-subscription-button.btn.btn-lg.btn-default(data-i18n="subscribe.never_mind") Never Mind, I Still Love You + button.confirm-end-subscription-button.btn.btn-lg.btn-warning(data-i18n="subscribe.confirm_unsubscribe") Confirm Unsubscribe + + table.table.table-striped.table-condensed + tr + th(data-i18n="user.status") Status + td + if personalSub.subscribed + strong(data-i18n="account.subscribed") + else + if personalSub.active + strong(data-i18n="account.active") + .text-muted(data-i18n="account.status_unsubscribed_active") + else + strong(data-i18n="account.unsubscribed") + .text-muted(data-i18n="account.status_unsubscribed") + if personalSub.activeUntil + tr + th(data-i18n="account.active_until") + td= moment(activeUntil).format('l') + if personalSub.nextPaymentDate + tr + th(data-i18n="account.next_payment") + td= moment(personalSub.nextPaymentDate).format('l') + if personalSub.cost + tr + th(data-i18n="account.cost") + td= personalSub.cost + if personalSub.card + tr + th(data-i18n="account.card") + td= personalSub.card + + else + button.start-subscription-button.btn.btn-lg.btn-success(data-i18n="subscribe.subscribe_title") Subscribe + if personalSub.free === true + div(data-i18n="subscribe.currently_free") + else if typeof personalSub.free === 'string' + if new Date() < new Date(personalSub.free) + div + span(data-i18n="subscribe.currently_free_until") + span.spl.spr= moment(new Date(personalSub.free)).format('l') + else + span(data-i18n="subscribe.was_free_until") + span.spl.spr= moment(new Date(personalSub.free)).format('l') + + //- Sponsored Subscriptions + + .panel.panel-default + .panel-heading + h3(data-i18n="subscribe.managed_subs") + p(data-i18n="subscribe.managed_subs_desc") + p(data-i18n="subscribe.managed_subs_desc_2") + h4(data-i18n="subscribe.group_discounts") + table.table.table-striped.table-condensed.discount-table + tr + td(data-i18n="subscribe.group_discounts_1st") + td(data-i18n="subscribe.group_discounts_full") + tr + td(data-i18n="subscribe.group_discounts_2nd") + td(data-i18n="subscribe.group_discounts_20") + tr + td(data-i18n="subscribe.group_discounts_12th") + td(data-i18n="subscribe.group_discounts_40") + .panel-body + if recipientSubs.state === 'subscribing' + .alert.alert-info(data-i18n="subscribe.subscribing") + else + textarea.recipient-emails(rows=3, data-i18n="[placeholder]subscribe.recipient_emails_placeholder") + div + button.recipients-subscribe-button.btn.btn-lg.btn-success(data-i18n="subscribe.subscribe_users") + if recipientSubs.state === 'declined' + br + .alert.alert-danger.alert-dismissible + span(data-i18n="buy_gems.declined") + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + else if recipientSubs.state === 'unknown_error' + br + .alert.alert-danger.alert-dismissible + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + p(data-i18n="loading_error.unknown") + p= recipientSubs.stateMessage + else if recipientSubs.justSubscribed && recipientSubs.justSubscribed.length > 0 + br + .alert.alert-success.alert-dismissible + if recipientSubs.justSubscribed.length > 0 + div(data-i18n="subscribe.users_subscribed") + ul + each email in recipientSubs.justSubscribed + li= email + else if recipientSubs.justSubscribed && recipientSubs.justSubscribed.length === 0 + br + .alert.alert-success.alert-dismissible + div(data-i18n="subscribe.no_users_subscribed") + + if recipientSubs.nextPaymentAmount > 0 && recipientSubs.sponsorSub + h4(data-i18n="account.next_payment") + p= moment(new Date(recipientSubs.sponsorSub.current_period_end * 1000)).format('l') + p $#{recipientSubs.nextPaymentAmount / 100} + p= recipientSubs.card + + h4(data-i18n="subscribe.current_recipients") + table.table.table-striped.table-condensed.recipients-table + tr + th(data-i18n="general.email") + th(data-i18n="general.name") + th + for recipient in recipientSubs.recipients + tr + td.recipient-email= recipient.emailLower + td.recipient-name= recipient.name + td.recipient-unsubscribe + if recipient.cancel_at_period_end + div Ends #{moment(recipient.cancel_at_period_end).format('l')} + else if recipientSubs.unsubscribingRecipients.indexOf(recipient.emailLower) >= 0 + div(data-i18n="subscribe.unsubscribing") + else + button.recipient-unsubscribe-button.btn.btn-sm.btn-warning Unsubscribe + button.confirm-recipient-unsubscribe-button.btn.btn-sm.btn-primary.hide(data-i18n="play.confirm") + diff --git a/app/templates/account/unsubscribe.jade b/app/templates/account/unsubscribe-view.jade similarity index 100% rename from app/templates/account/unsubscribe.jade rename to app/templates/account/unsubscribe-view.jade diff --git a/app/templates/account/wizard_settings.jade b/app/templates/account/wizard_settings.jade deleted file mode 100644 index 192f758c4..000000000 --- a/app/templates/account/wizard_settings.jade +++ /dev/null @@ -1,18 +0,0 @@ -#color-settings - table.table.table-bordered.table-hover - tr - th(data-i18n="wizard_settings.active") Active - th(data-i18n="wizard_settings.color") Color - th(data-i18n="wizard_settings.group") Group - for group in colorGroups - tr.color-group(data-name=group.name) - td.enabled-cell - input(type='checkbox', checked=group.exists, id=group.name).color-group-checkbox - td.color-cell - input.minicolors(type=hidden, value=group.rgb, name=group.name) - td.group-cell - label(for=group.name, data-i18n='wizard_settings.' + group.dasherized)= group.humanized - -canvas#tinting-display(width=200, height=200).img-rounded - -div.clearfix \ No newline at end of file diff --git a/app/templates/admin.jade b/app/templates/admin.jade index 36d754468..f5817aa9a 100644 --- a/app/templates/admin.jade +++ b/app/templates/admin.jade @@ -25,16 +25,18 @@ block content h4(data-i18n="admin.av_entities_sub_title") Entities ul - li - a(href="/admin/users", data-i18n="admin.av_entities_users_url") Users li a(href="/admin/level-sessions", data-i18n="admin.av_entities_active_instances_url") Active Instances - li - a(href="/admin/employers", data-i18n="admin.av_entities_employer_list_url") Employer List li a(href="/admin/candidates", data-i18n="admin.av_entities_candidates_list_url") Candidate List + li + a(href="/admin/employers", data-i18n="admin.av_entities_employer_list_url") Employer List + li + a(href="/admin/trial-requests") Trial Requests li a(href="/admin/user-code-problems", data-i18n="admin.av_entities_user_code_problems_list_url") User Code Problems List + li + a(href="/admin/users", data-i18n="admin.av_entities_users_url") Users h4(data-i18n="admin.av_other_sub_title") Other @@ -43,9 +45,23 @@ block content a(href="/admin/base", data-i18n="admin.av_other_debug_base_url") Base (for debugging base.jade) li a(href="/admin/clas", data-i18n="admin.clas") CLAs + li + a(href="/admin/pending-patches", data-i18n="resources.patches") Patches if me.isAdmin() - li - a(href="/admin/growth", data-i18n="admin.growth") Growth + li Analytics + ul + li + a(href="/admin/analytics/subscriptions") Subscriptions + li + a(href="/admin/analytics/users") Users (needs updating) + + if me.isAdmin() + hr + h3 Prepaids + a.btn.btn-secondary#create-free-sub-btn Create Free Subscription Link + span.spl.spr + if freeSubLink + input#free-sub-input(type="text", readonly, value="#{freeSubLink}") hr diff --git a/app/templates/admin/administer-user-modal.jade b/app/templates/admin/administer-user-modal.jade new file mode 100644 index 000000000..75a6991b2 --- /dev/null +++ b/app/templates/admin/administer-user-modal.jade @@ -0,0 +1,37 @@ +extends /templates/core/modal-base + +block modal-header-content + h3 Administer User + h4 #{user.get('name') || 'Unnamed'} / #{user.get('email')} + span= user.id + + +block modal-body-content + + h3 Stripe Benefit + .form + .form-group + .radio + label + input(type="radio" name="stripe-benefit" value="" checked=none) + | None + .radio + label + input(type="radio" name="stripe-benefit" value="free" checked=free) + | Free + .radio + label + input(type="radio" name="stripe-benefit" value="free-until" checked=FreeUntil) + | Free Until + input.form-control.spl(type="date" name="stripe-free-until" value=freeUntilDate)#free-until-date + .radio + label + input(type="radio" name="stripe-benefit" value="coupon" checked=coupon) + | Coupon + select.form-control#coupon-select + for couponOption in coupons + option(value=couponOption.id selected=coupon===couponOption.id)= couponOption.format + +block modal-footer-content + button#save-changes.btn.btn-primary Save Changes + diff --git a/app/templates/admin/analytics-subscriptions.jade b/app/templates/admin/analytics-subscriptions.jade new file mode 100644 index 000000000..df0befe0e --- /dev/null +++ b/app/templates/admin/analytics-subscriptions.jade @@ -0,0 +1,159 @@ +extends /templates/base + +block content + + if !me.isAdmin() + div You must be logged in as an admin to view this page. + else + + if total === 0 + h4= refreshDataState + else + .container-fluid + .row + .col-md-5.big-stat.total-count + div.description Total + div.count= total + .col-md-5.big-stat.remaining-count + div.description Remaining + div.count= total - outstandingCancels.length + .col-md-5.big-stat.cancelled-count + div.description Cancels Outstanding + div.count= outstandingCancels.length + .col-md-5.big-stat.growth-rate + div.description 30 Day Total Growth + div.count #{monthlyGrowth.toFixed(1)}% + .col-md-5.big-stat.churn-count + div.description Monthly Churn (cancelled / total) + div.count #{monthlyChurn.toFixed(1)}% + + each graph in analytics.graphs + .line-graph-container + each line in graph.lines + each point in line.points + .graph-point-info-container(data-pointid="#{point.pointID}") + div(style='font-weight:bold;') #{point.day} + each value in point.values + div #{value} + + h2 Recent Subscribers + if !subscribers || subscribers.length < 1 + h4= refreshDataState + else + table.table.table-striped.table-condensed + thead.subscribers-thead + tr + th Sub ID + th User Start + th Sub Start + if subscriberCancelled + th Cancelled + else + th + th Conversion + th Email + th Hero + th Level + th Age + th Spoken + th Clans + tbody.subscribers-tbody + each subscriber in subscribers + tr + td + a(href="https://dashboard.stripe.com/customers/#{subscriber.customerID}", target="_blank")= subscriber.subscriptionID + td= subscriber.user.dateCreated.substring(0, 10) + td= subscriber.start.toISOString().substring(0, 10) + td + if subscriber.cancel + span= subscriber.cancel.toISOString().substring(0, 10) + td + if subscriber.user.stripe && subscriber.user.stripe.sponsorID + span *sponsored* + else if subscriber.user.conversion + span= subscriber.user.conversion + if subscriber.user.deleted + td DELETED + else + td= subscriber.user.emailLower + td= subscriber.hero + td= subscriber.level + td= subscriber.user.ageRange + td= subscriber.user.preferredLanguage + if subscriber.user.clans + td= subscriber.user.clans.length + else + td + + h2 Recent Cancellations + if !cancellations || cancellations.length < 1 + h4= refreshDataState + else + table.table.table-striped.table-condensed + thead.subscribers-thead + tr + th Sub ID + th User ID + th User Start + th Sub Start + th Sub Cancel + th Length + th Level + th Age + th Spoken + th Clans + tbody.subscribers-tbody + each cancellation in cancellations + tr + td + a(href="https://dashboard.stripe.com/customers/#{cancellation.customerID}", target="_blank")= cancellation.subscriptionID + if cancellation.userID + td + a(href="/user/#{cancellation.userID}")= cancellation.userID + else + td + if cancellation.user + td= cancellation.user.dateCreated.substring(0, 10) + else + td + td= cancellation.start.toISOString().substring(0, 10) + td= cancellation.cancel.toISOString().substring(0, 10) + td= moment.duration(cancellation.cancel - cancellation.start).humanize() + td= cancellation.level + if cancellation.user + td= cancellation.user.ageRange + td= cancellation.user.preferredLanguage + if cancellation.user.clans + td= cancellation.user.clans.length + else + td + else + td + td + td + td + + h2 Subscriptions + if !subs || subs.length < 1 + h4= refreshDataState + else + table.table.table-condensed + thead + tr + th Day + th Total + th Started + th Cancelled + th Net (cancelled) + th Ended + th Net (ended) + tbody + each sub in subs + tr + td= sub.day + td= sub.total + td= sub.started + td= sub.cancelled + td= sub.started - sub.cancelled + td= sub.ended + td= sub.started - sub.ended diff --git a/app/templates/admin/growth.jade b/app/templates/admin/analytics-users.jade similarity index 92% rename from app/templates/admin/growth.jade rename to app/templates/admin/analytics-users.jade index 0ff7a3c1d..982281a24 100644 --- a/app/templates/admin/growth.jade +++ b/app/templates/admin/analytics-users.jade @@ -2,10 +2,10 @@ extends /templates/base block content - h1(data-i18n="admin.growth_title") Growth + h1(data-i18n="admin.growth_title") Users if me.isAdmin() if crunchingData - h4 Cruncing Data.. + h4 Crunching Data.. else h2 Registered Users h3 Per-Day diff --git a/app/templates/admin/pending-patches-view.jade b/app/templates/admin/pending-patches-view.jade new file mode 100644 index 000000000..2cc7de051 --- /dev/null +++ b/app/templates/admin/pending-patches-view.jade @@ -0,0 +1,20 @@ +extends /templates/base + +block content + + h1(data-i18n="resources.patches") Patches + + table.table.table-striped.table-bordered.table-condensed#patches + tbody + each patch in patches + tr + td #{patch.target.collection} + + td + if patch.url + a(href=patch.url)= patch.name + else + span= patch.target.original + td #{patch.creatorName} + td #{patch.commitMessage} + \ No newline at end of file diff --git a/app/templates/admin/trial-requests.jade b/app/templates/admin/trial-requests.jade new file mode 100644 index 000000000..eb8ef989e --- /dev/null +++ b/app/templates/admin/trial-requests.jade @@ -0,0 +1,54 @@ +extends /templates/base + +block content + + if !me.isAdmin() + div You must be logged in as an admin to view this page. + + else + h2 Trial Requests + if !trialRequests || trialRequests.length < 1 + h4 Fetching trial requests... + else + table.table.table-condensed + thead + tr + th Created + th Reviewed + th Applicant + th School + th Location + th Age + th Students + th How Found + th Status + tbody + - var numReviewed = 0 + - var maxReviewedShown = 100 + each trialRequest in trialRequests + if trialRequest.get('status') !== 'submitted' + - numReviewed++ + if numReviewed > maxReviewedShown + - break + tr + td.created= new Date(parseInt(trialRequest.get('_id').substring(0, 8), 16) * 1000).toISOString().substring(0, 10) + td.reviewed + if trialRequest.get('reviewDate') + span= trialRequest.get('reviewDate').substring(0, 10) + td + a(href="/user/#{trialRequest.get('applicant')}")= trialRequest.get('properties').email + td= trialRequest.get('properties').school + td= trialRequest.get('properties').location + td= trialRequest.get('properties').age + td= trialRequest.get('properties').numStudents + td= trialRequest.get('properties').heardAbout + td.status-cell + if trialRequest.get('status') === 'submitted' + button.btn.btn-xs.btn-success.btn-approve(data-trial-request-id=trialRequest.id) Approve + button.btn.btn-xs.btn-danger.btn-deny(data-trial-request-id=trialRequest.id) Deny + else if trialRequest.get('prepaidCode') + span= trialRequest.get('prepaidCode') + else + span= trialRequest.get('status') + + div *Currently assumes all trial requests of type 'subscription' diff --git a/app/templates/base.jade b/app/templates/base.jade index eff8dd35c..e3eba21f4 100644 --- a/app/templates/base.jade +++ b/app/templates/base.jade @@ -8,11 +8,12 @@ block header a(href="/") span.glyphicon.glyphicon-home a(href="/about", data-i18n="nav.about") - a(href='/play/ladder', data-i18n="home.multiplayer") - a(href='/community', data-i18n="nav.community") - a(href='http://blog.codecombat.com/', data-i18n="nav.blog") + a(href='/teachers', data-i18n="nav.teachers") Teachers + a(href='/clans', data-i18n="clans.clans") Clans a(href='http://discourse.codecombat.com/', data-i18n="nav.forum") - + a(href='/community', data-i18n="nav.community") + //a(href='/play/ladder', data-i18n="home.multiplayer").multiplayer-nav-link + if me.get('anonymous') === false span.dropdown button.btn.btn-sm.header-font.dropdown-toggle(href="#", data-toggle="dropdown") @@ -26,7 +27,7 @@ block header li.user-dropdown-header span.user-level= me.level() a(href="/user/#{me.getSlugOrID()}") - img.img-circle(src="#{me.getPhotoURL()}" alt="") + div.img-circle(style="background-image: url(#{me.getPhotoURL()})") h3=me.displayName() li a(href="/user/#{me.getSlugOrID()}" data-i18n="nav.profile") @@ -34,6 +35,8 @@ block header a(href="/account/settings", data-i18n="play.settings") li a(href="/account/payments", data-i18n="account.payments") + li + a(href="/account/subscription", data-i18n="account.subscription") li a#logout-button(data-i18n="login.log_out") @@ -55,11 +58,10 @@ block footer img#footer-background(src="/images/pages/base/nav_background.png") #footer-links + a(tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="nav.contact") Contact + a(href='http://blog.codecombat.com/', data-i18n="nav.blog") a(href='/contribute', tabindex=-1, data-i18n="nav.contribute") Contribute a(href='/legal', tabindex=-1, data-i18n="nav.legal") Legal - a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="nav.contact") Contact - a(href='/teachers', data-i18n="nav.teachers") Teachers - a(href="/play-old", data-i18n="play.older_campaigns") Older Campaigns if me.isAdmin() a(href='/admin', data-i18n="nav.admin") Admin @@ -70,21 +72,19 @@ block footer .fb-like(data-href="https://www.facebook.com/codecombat", data-send="false", data-layout="button_count", data-width="350", data-show-faces="true", data-ref="coco_footer_#{fbRef}") if !isIE a.twitter-follow-button(href="https://twitter.com/CodeCombat", data-show-count="true", data-show-screen-name="false", data-dnt="true", data-align="right", data-i18n="nav.twitter_follow") Follow - iframe.github-star-button(src="http://ghbtns.com/github-btn.html?user=codecombat&repo=codecombat&type=watch&count=true", allowtransparency="true", frameborder="0", scrolling="0", width="110", height="20") + iframe.github-star-button(src="https://ghbtns.com/github-btn.html?user=codecombat&repo=codecombat&type=watch&count=true", allowtransparency="true", frameborder="0", scrolling="0", width="110", height="20") #footer-credits - a.mixpanel-badge(href="https://mixpanel.com/f/partner") - img(src="//cdn.mxpnl.com/site_media/images/partner/badge_light.png", alt="Mobile Analytics") span span © All Rights Reserved br - span CodeCombat 2014 + span CodeCombat 2015 img#footer-logo(src="/images/pages/base/logo.png", alt="CodeCombat") span span Site Design by br a(href="http://www.fullyillustrated.com/") Fully Illustrated - a.firebase-bade(href="https://www.firebase.com/") - img(src="/images/pages/base/firebase.png", alt="Powered by Firebase") + //a.firebase-bade(href="https://www.firebase.com/") // Not using right now + // img(src="/images/pages/base/firebase.png", alt="Powered by Firebase") block extra_footer_content diff --git a/app/templates/cla.jade b/app/templates/cla.jade index cdf375968..bda7e1f09 100644 --- a/app/templates/cla.jade +++ b/app/templates/cla.jade @@ -83,7 +83,7 @@ p strong You must be signed in to sign this agreement. - button.btn.btn-primary.auth-button + button.btn.btn-primary.login-button span(data-i18n="login.log_in") Log In span.spr.spl / span(data-i18n="login.sign_up") Create Account diff --git a/app/templates/clans/clan-details.jade b/app/templates/clans/clan-details.jade new file mode 100644 index 000000000..ab1548311 --- /dev/null +++ b/app/templates/clans/clan-details.jade @@ -0,0 +1,223 @@ +extends /templates/base + +block content + + .modal#editNameModal + .modal-dialog + .modal-header + button.close(data-dismiss='modal') + span × + h3.modal-title(data-i18n="clans.edit_clan_name") Edit Clan Name + .modal-body + input.edit-name-input(type='text', value="#{clan.get('name')}") + .modal-footer + button.btn(data-dismiss='modal', data-i18n="modal.close") Close + button.btn.edit-name-save-btn(data-i18n="common.save_changes") Save changes + + .modal#editDescriptionModal + .modal-dialog + .modal-header + button.close(data-dismiss='modal') + span × + h3.modal-title(data-i18n="clans.edit_clan_description") Edit Clan Description + .modal-body + textarea.edit-description-input(rows=2)= clan.get('description') + .modal-footer + button.btn(data-dismiss='modal', data-i18n="modal.close") Close + button.btn.edit-description-save-btn(data-i18n="common.save_changes") Save changes + + if clan + h1 #{clan.get('name')} + if clan.get('type') === 'private' + small(data-i18n="clans.private") (private) + if clan.get('ownerID') === me.id + span.spl + button.btn.btn-xs.edit-name-btn(data-toggle='modal', data-target='#editNameModal', data-i18n="clans.edit_name") edit name + + if clan.get('description') + .clan-description + each line in clan.get('description').split('\n') + p= line + if clan.get('ownerID') === me.id + button.btn.btn-xs.edit-description-btn(data-toggle='modal', data-target='#editDescriptionModal', data-i18n="clans.edit_description") edit description + + h5(data-i18n="clans.summary") Summary + table.table.table-condensed.stats-table + if owner + tr + td + span.spr(data-i18n="clans.chieftain") Chieftain + td + span.spr.player-hero-icon(data-memberid="#{clan.get('ownerID')}") + a(href="/user/#{clan.get('ownerID')}")= owner.get('name') + if stats.averageLevel + tr + td(data-i18n="clans.average_level") Average Level + td= stats.averageLevel + if stats.averageAchievements && clan.get('type') === 'public' + tr + td(data-i18n="clans.average_achievements") Average Achievements + td= stats.averageAchievements + + p + if isOwner + button.btn.btn-xs.btn-warning.delete-clan-btn(data-i18n="clans.delete_clan") Delete Clan + else if isMember + button.btn.btn-xs.btn-warning.leave-clan-btn(data-i18n="clans.leave_clan") Leave Clan + else + button.btn.btn-lg.btn-success.join-clan-btn(data-i18n="clans.join_clan") Join Clan + + if clan.get('ownerID') === me.id || clan.get('type') === 'public' + div + span.spl.spr.join-link-prompt(data-i18n="clans.invite_1") Invite: + input.join-clan-link(type="text", readonly, value="#{joinClanLink}") + .small(data-i18n="clans.invite_2") *Invite players to this Clan by sending them this link. + + if members + h3 + span.spr(data-i18n="clans.members") Members + span (#{members.length}) + + //- Premium dashboard + if clan.get('dashboardType') === 'premium' + table.table.table-condensed + thead + tr + th + span.member-header.spr(data-i18n="resources.hero") Hero + if memberSort === 'nameAsc' + span.member-header.glyphicon.glyphicon-chevron-up + else if memberSort === 'nameDesc' + span.member-header.glyphicon.glyphicon-chevron-down + th + span.progress-header.spr(data-i18n="clans.progress") Progress + if memberSort === 'progressAsc' + span.progress-header.glyphicon.glyphicon-chevron-up + else if memberSort === 'progressDesc' + span.progress-header.glyphicon.glyphicon-chevron-down + else + span(style='padding-left:16px;') + span.spl.progress-key.progress-key-complete(data-i18n="clans.complete_1") complete + span.progress-key.progress-key-started(data-i18n="clans.started_1") started + span.progress-key(data-i18n="clans.not_started_1") not started + input.expand-progress-checkbox(type='checkbox') + span.spl.expand-progress-label(data-i18n="clans.exp_levels") Expand levels + tbody + each member in members + tr + td + div + span.hero-icon-cell + span.spr.player-hero-icon(data-memberid="#{member.id}") + span.code-language-cell + if memberLanguageMap && memberLanguageMap[member.id] + span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id]) + div + a(href="/user/#{member.id}")= member.get('name') || 'Anoner' + div Level #{member.level()} + if isOwner && member.id !== clan.get('ownerID') + button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}", data-i18n="clans.rem_hero") Remove Hero + td.progress-cell + .level-progression-concepts Concepts + each concept in conceptsProgression + if userConceptsMap[member.id] && userConceptsMap[member.id][concept] === 'complete' + span.spr.progress-level-cell.progress-level-cell-complete(data-i18n="concepts." + concept) + else if userConceptsMap[member.id] && userConceptsMap[member.id][concept] === 'started' + span.spr.progress-level-cell.progress-level-cell-started(data-i18n="concepts." + concept) + else + span.spr.progress-level-cell.progress-level-cell-not-started(data-i18n="concepts." + concept) + .level-progression-levels Levels + each campaign in campaignLevelProgressions + if lastUserCampaignLevelMap[member.id] && lastUserCampaignLevelMap[member.id][campaign.ID] + div.level-progression-campaign= campaign.name + - var i = 0 + + each level in campaign.levels + - i++ + - var state = null, levelInfo = null + if memberLevelStateMap[member.id][level.slug] + - levelInfo = memberLevelStateMap[member.id][level.slug].levelInfo + - state = memberLevelStateMap[member.id][level.slug].state + if state === 'complete' + span.progress-level-cell.progress-level-cell-complete(data-level-info=levelInfo) #{i} + if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1 + span.spl #{level.name} + .level-popup-container + h3 #{i}. #{levelInfo.level} + p + div + span(data-i18n="clans.status") Status + span.spr : + span(data-i18n="clans.complete_2") Complete + div + span(data-i18n="clans.playtime") Playtime + span : #{levelInfo.playtime}s + div + span(data-i18n="clans.last_played") Last played + span : #{levelInfo.changed} + if isOwner || me.isAdmin() + strong(data-i18n="clans.view_solution") Click to view solution. + else if state === 'started' + span.progress-level-cell.progress-level-cell-started(data-level-info=levelInfo) #{i} + if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1 + span.spl #{level.name} + .level-popup-container + h3 #{i}. #{level.name} + p + div + span(data-i18n="clans.status") Status + span.spr : + span(data-i18n="clans.started_2") Started + div + span(data-i18n="clans.playtime") Playtime + span : #{levelInfo.playtime}s + div + span(data-i18n="clans.last_played") Last played + span : #{levelInfo.changed} + if isOwner || me.isAdmin() + strong(data-i18n="clans.view_solution") Click to view solution. + else + span.progress-level-cell.level-progression-level-not-started #{i} + if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1 + span.spl #{level.name} + .level-popup-container + h3 #{i}. #{level.name} + div + span(data-i18n="clans.status") Status + span.spr : + span(data-i18n="clans.not_started_2") Not Started + if lastUserCampaignLevelMap[member.id][campaign.ID].levelSlug === level.slug + - break + + //- Basic dashboard + else + table.table.table-striped.table-condensed + thead + tr + th + th + th.name-cell(data-i18n="clans.name") Name + th.level-cell(data-i18n="resources.level") Level + th.achievements-cell(data-i18n="play.achievements") Achievements + th.latest-achievement-cell(data-i18n="clans.latest_achievement") Latest Achievement + th.remove-member-cell + tbody + each member in members + tr + td.hero-icon-cell + span.spr.player-hero-icon(data-memberid="#{member.id}") + td.code-language-cell + if memberLanguageMap && memberLanguageMap[member.id] + span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id]) + td.name-cell + a(href="/user/#{member.id}")= member.get('name') || 'Anoner' + td.level-cell= member.level() + td.achievements-cell + if memberAchievementsMap && memberAchievementsMap[member.id] + | #{memberAchievementsMap[member.id].length} + td.latest-achievement-cell + if memberAchievementsMap && memberAchievementsMap[member.id] && memberAchievementsMap[member.id].length + span= memberAchievementsMap[member.id][0].get('achievementName') + td.remove-member-cell + if isOwner && member.id !== clan.get('ownerID') + button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}", data-i18n="clans.rem_hero") Remove Hero diff --git a/app/templates/clans/clans.jade b/app/templates/clans/clans.jade new file mode 100644 index 000000000..a0c14444d --- /dev/null +++ b/app/templates/clans/clans.jade @@ -0,0 +1,82 @@ +extends /templates/base + +block content + + p + input.create-clan-name(type='text', data-i18n="[placeholder]clans.new_name", placeholder='New clan name') + p + textarea.create-clan-description(rows=2, data-i18n="[placeholder]clans.new_description", placeholder='New clan description') + p + input(type='checkbox').private-clan-checkbox + span.spl(data-i18n="clans.make_private") Make clan private + span.spl ( + a.private-more-info(data-i18n="clans.private_preview") + span ) + p + button.btn.btn-success.create-clan-btn(data-i18n="clans.create_clan") Create New Clan + + div(role='tabpanel') + ul.nav.nav-tabs(role='tablist') + li.active(role='presentation') + a(href='#public-clans', aria-controls='public-clans', role='tab', data-toggle='tab', data-i18n="clans.public_clans") Public Clans + li(role='presentation') + a(href='#my-clans', aria-controls='my-clans', role='tab', data-toggle='tab', data-i18n="clans.my_clans") My Clans + + .tab-content + .tab-pane.active#public-clans(role='tabpanel') + table.table.table-striped.table-condensed + thead + tr + th(data-i18n="clans.clan_name") Clan Name + th(data-i18n="play.heroes") Heroes + th(data-i18n="clans.chieftain") Chieftain + th + tbody + if publicClans.length + each clan in publicClans + tr + td + if clan.get('ownerID') === me.id + a(href="/clans/#{clan.id}", style='font-weight:bold')= clan.get('name') + else + a(href="/clans/#{clan.id}")= clan.get('name') + td= clan.get('members').length + td + if idNameMap && idNameMap[clan.get('ownerID')] + a(href="/user/#{clan.get('ownerID')}")= idNameMap[clan.get('ownerID')] + else + a(href="/user/#{clan.get('ownerID')}") Anoner + td + if myClanIDs.indexOf(clan.id) < 0 + button.btn.btn-success.join-clan-btn(data-id="#{clan.id}", data-i18n="clans.join_clan") Join Clan + else if clan.get('ownerID') !== me.id + button.btn.btn-xs.btn-warning.leave-clan-btn(data-id="#{clan.id}", data-i18n="clans.leave_clan") Leave Clan + + .tab-pane#my-clans(role='tabpanel') + table.table.table-striped.table-condensed + thead + tr + th(data-i18n="clans.clan_name") Clan Name + th(data-i18n="play.heroes") Heroes + th(data-i18n="clans.chieftain") Chieftain + th(data-i18n="clans.type") Type + th + tbody + if myClans.length + each clan in myClans + tr + td + if clan.get('ownerID') === me.id + a(href="/clans/#{clan.id}", style='font-weight:bold')= clan.get('name') + else + a(href="/clans/#{clan.id}")= clan.get('name') + td= clan.get('members').length + td + if idNameMap && idNameMap[clan.get('ownerID')] + a(href="/user/#{clan.get('ownerID')}")= idNameMap[clan.get('ownerID')] + else + a(href="/user/#{clan.get('ownerID')}") Anoner + td= clan.get('type') + td + if clan.get('ownerID') !== me.id + button.btn.btn-xs.btn-warning.leave-clan-btn(data-id="#{clan.id}", data-i18n="clans.leave_clan") Leave Clan diff --git a/app/templates/common/search.jade b/app/templates/common/search-view.jade similarity index 54% rename from app/templates/common/search.jade rename to app/templates/common/search-view.jade index 2581ddb8d..2bb4559cd 100644 --- a/app/templates/common/search.jade +++ b/app/templates/common/search-view.jade @@ -9,10 +9,11 @@ block content li.active(data-i18n="#{currentEditor}") | #{currentEditor} - if me.get('anonymous') - a.btn.btn-primary.open-modal-button(data-toggle="coco-modal", data-target="core/AuthModal", role="button", data-i18n="#{currentNewSignup}") Log in to Create a New Content - else - a.btn.btn-primary.open-modal-button#new-model-button(data-i18n="#{currentNew}") Create a New Something + if me.isAdmin() || !newModelsAdminOnly + if me.get('anonymous') + a.btn.btn-primary.open-modal-button(data-toggle="coco-modal", data-target="core/AuthModal", role="button", data-i18n="#{currentNewSignup}") Log in to Create a New Something + else + a.btn.btn-primary.open-modal-button#new-model-button(data-i18n="#{currentNew}") Create a New Something input#search(data-i18n="[placeholder]#{currentSearch}") hr div.results diff --git a/app/templates/community.jade b/app/templates/community-view.jade similarity index 60% rename from app/templates/community.jade rename to app/templates/community-view.jade index 238a8f11c..b13039bc2 100644 --- a/app/templates/community.jade +++ b/app/templates/community-view.jade @@ -12,31 +12,31 @@ block content a(href="/editor/level") img(src="/images/pages/community/level.png") h2 - a.spl(href="/editor/level", data-i18n="editor.level_title") + a(href="/editor/level", data-i18n="editor.level_title") p - span(data-i18n="community.level_editor_prefix") Use the CodeCombat - a.spl.spr(href="/editor/level", data-i18n="editor.level_title") - span(data-i18n="community.level_editor_suffix") to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours! + span.spr(data-i18n="community.level_editor_prefix") Use the CodeCombat + a(href="/editor/level", data-i18n="editor.level_title") + span.spl(data-i18n="community.level_editor_suffix") to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours! .community-columns a(href="/editor/thang") img(src="/images/pages/community/thang.png") h2 - a.spl(href="/editor/thang", data-i18n="editor.thang_title") + a(href="/editor/thang", data-i18n="editor.thang_title") p - span(data-i18n="community.thang_editor_prefix") We call units within the game 'thangs'. Use the - a.spl.spr(href="/editor/thang", data-i18n="editor.thang_title") - span(data-i18n="community.thang_editor_suffix") to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites. + span.spr(data-i18n="community.thang_editor_prefix") We call units within the game 'thangs'. Use the + a(href="/editor/thang", data-i18n="editor.thang_title") + span.spl(data-i18n="community.thang_editor_suffix") to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites. .community-columns a(href="/editor/article") img(src="/images/pages/community/article.png") h2 - a.spl(href="/editor/article", data-i18n="editor.article_title") + a(href="/editor/article", data-i18n="editor.article_title") p - span(data-i18n="community.article_editor_prefix") See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the - a.spl.spr(href="/editor/article", data-i18n="editor.article_title") - span(data-i18n="community.article_editor_suffix") and help CodeCombat players get the most out of their playtime. + span.spr(data-i18n="community.article_editor_prefix") See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the + a(href="/editor/article", data-i18n="editor.article_title") + span.spl(data-i18n="community.article_editor_suffix") and help CodeCombat players get the most out of their playtime. div @@ -72,24 +72,24 @@ block content .logo-row.contribute-classes - a(href="/contribute#adventurer") - img(src="/images/pages/community/adventurer.png") - - a(href="/contribute#ambassador") - img(src="/images/pages/community/ambassador.png") - - a(href="/contribute#archmage") + a(href="/contribute/archmage") img(src="/images/pages/community/archmage.png") - a(href="/contribute#scribe") + a(href="/contribute/artisan") + img(src="/images/pages/community/artisan.png") + + if !me.get('chinaVersion') + a(href="/contribute/adventurer") + img(src="/images/pages/community/adventurer.png") + + a(href="/contribute/scribe") img(src="/images/pages/community/scribe.png") - a(href="/contribute#diplomat") + a(href="/contribute/diplomat") img(src="/images/pages/community/diplomat.png") - a(href="/contribute#artisan") - img(src="/images/pages/community/artisan.png") + a(href="/contribute/ambassador") + img(src="/images/pages/community/ambassador.png") .clearfix - diff --git a/app/templates/contribute/adventurer.jade b/app/templates/contribute/adventurer.jade index 3b9367620..8be546672 100644 --- a/app/templates/contribute/adventurer.jade +++ b/app/templates/contribute/adventurer.jade @@ -4,12 +4,11 @@ block content div.contribute_class - include /templates/contribute/contribute_nav + .class_detail + + img(src="/images/pages/contribute/class_detail_adventurer.png", alt="") div.class-main#adventurer-main - - .class_image - img.img-responsive(src="/images/pages/contribute/adventurer.png", alt="") h2 span(data-i18n="classes.adventurer_title") Adventurer diff --git a/app/templates/contribute/ambassador.jade b/app/templates/contribute/ambassador.jade index 735355fa6..294b77ea9 100644 --- a/app/templates/contribute/ambassador.jade +++ b/app/templates/contribute/ambassador.jade @@ -4,12 +4,11 @@ block content div.contribute_class - include /templates/contribute/contribute_nav + .class_detail + + img(src="/images/pages/contribute/class_detail_ambassador.png", alt="") div.class-main#ambassador-main - - .class_image - img.img-responsive(src="/images/pages/contribute/ambassador.png", alt="") h2 span(data-i18n="classes.ambassador_title") Ambassador @@ -31,7 +30,7 @@ block content h4(data-i18n="contribute.how_to_join") How to Join p - a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="contribute.contact_us_url") + a(tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="contribute.contact_us_url") | Contact us span , span(data-i18n="contribute.ambassador_join_desc") diff --git a/app/templates/contribute/archmage.jade b/app/templates/contribute/archmage.jade index 2518d93fc..200a2e454 100644 --- a/app/templates/contribute/archmage.jade +++ b/app/templates/contribute/archmage.jade @@ -4,13 +4,12 @@ block content div.contribute_class - include /templates/contribute/contribute_nav + .class_detail + + img(src="/images/pages/contribute/class_detail_archmage.png", alt="") div.class-main#archmage-main - .class_image - img.img-responsive(src="/images/pages/contribute/archmage.png", alt="") - h2 span(data-i18n="classes.archmage_title") Archmage span @@ -42,13 +41,13 @@ block content p span(data-i18n="contribute.join_desc_1") | Anyone can help out! Just check out our - a(title='GitHub', href="https://github.com/codecombat/codecombat", tabindex=-1) + a(href="https://github.com/codecombat/codecombat", tabindex=-1) | GitHub span span(data-i18n="contribute.join_desc_2") | to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. | Want to chat about what to do or how to get more deeply involved? - a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="contribute.join_url_email") + a(tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="contribute.join_url_email") | Email us span(data-i18n="contribute.join_desc_3") | , or find us in our diff --git a/app/templates/contribute/artisan.jade b/app/templates/contribute/artisan.jade index 54a27327b..47a9438be 100644 --- a/app/templates/contribute/artisan.jade +++ b/app/templates/contribute/artisan.jade @@ -4,12 +4,11 @@ block content div.contribute_class - include /templates/contribute/contribute_nav + .class_detail + + img(src="/images/pages/contribute/class_detail_artisan.png", alt="") div.class-main#artisan-main - - .class_image - img.img-responsive(src="/images/pages/contribute/artisan.png", alt="") h2 span(data-i18n="classes.artisan_title") Artisan diff --git a/app/templates/contribute/contribute.jade b/app/templates/contribute/contribute.jade index 5fdb19698..78a0580c6 100644 --- a/app/templates/contribute/contribute.jade +++ b/app/templates/contribute/contribute.jade @@ -2,181 +2,85 @@ extends /templates/base block content - div.contribute_class + h2(data-i18n="contribute.page_title") Contributing + p(data-i18n="contribute.intro_blurb") + | CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game + | into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the + | world to code! - include /templates/contribute/contribute_nav + a(href="/contribute/archmage") + div.class_tile + img(src="/images/pages/contribute/tile_archmage.png", alt="") - div#contribute-main.class-main - div#intro + div.class_text + h3 + span.spr(data-i18n="classes.archmage_title") Archmage + span(data-i18n="classes.archmage_title_description") - h2(data-i18n="contribute.page_title") Contributing + p(data-i18n="classes.archmage_summary") + | If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat! - #homepage_screenshot - img.img-responsive(src="/images/pages/contribute/contribute_header.png", alt="") + a(href="/contribute/artisan") + div.class_tile + img.tile-img(src="/images/pages/contribute/tile_artisan.png", alt="") - p - strong(data-i18n="contribute.introduction_desc_intro") - | We have high hopes for CodeCombat. - | - span(data-i18n="contribute.introduction_desc_pref") - | We want to be where programmers of all stripes come to learn and play together, - | introduce others to the wonderful world of coding, - | and reflect the best parts of the community. - | We can't and don't want to do that alone; - | what makes projects like GitHub, Stack Overflow and Linux great are the people who - | use them and build on them. - | To that end, - a(href="https://github.com/codecombat/codecombat", data-i18n="contribute.introduction_desc_github_url") - | CodeCombat is totally open source - span(data-i18n="contribute.introduction_desc_suf") - | , and we aim to provide as many ways as possible for you to take part and - | make this project as much yours as ours. - p(data-i18n="contribute.introduction_desc_ending") - | We hope you'll join our party! - p(data-i18n="contribute.introduction_desc_signature").signature - | - Nick, George, Scott, Michael, Jeremy and Glen - hr + div.class_text + h3 + span.spr(data-i18n="classes.artisan_title") Artisan + span(data-i18n="classes.artisan_title_description") - .contributor-signup-anonymous + p(data-i18n="classes.artisan_summary") + | Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program. - #archmage.header-scrolling-fix - .class_image - img.img-responsive(src="/images/pages/contribute/archmage.png", alt="") + if !me.get('chinaVersion') + a(href="/contribute/adventurer") + div.class_tile + img.tile-img(src="/images/pages/contribute/tile_adventurer.png", alt="") + + div.class_text + h3 + span.spr(data-i18n="classes.adventurer_title") Adventurer + span(data-i18n="classes.adventurer_title_description") + + p(data-i18n="classes.adventurer_summary") + | Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release. - h3.header-scrolling-fix - span(data-i18n="classes.archmage_title") Archmage - span - span(data-i18n="classes.archmage_title_description") (Coder) - p(data-i18n="contribute.archmage_summary") - | Interested in working on game graphics, user interface design, database and server organization, - | multiplayer networking, physics, sound, or game engine performance? Want to help build a game to - | help other people learn what you are good at? We have a lot to do and if you are an experienced - | programmer and want to develop for CodeCombat, this class is for you. We would love your help - | building the best programming game ever. + a(href="/contribute/scribe") + div.class_tile + img.tile-img(src="/images/pages/contribute/tile_scribe.png", alt="") - a(href="/contribute/archmage") - p.lead(data-i18n="contribute.more_about_archmage") - | Learn More About Becoming an Archmage + div.class_text + h3 + span.spr(data-i18n="classes.scribe_title") Scribe + span(data-i18n="classes.scribe_title_description") - .contributor-signup(data-contributor-class-id="developer", data-contributor-class-name="archmage") + p(data-i18n="classes.scribe_summary") + | Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe. - #artisan.header-scrolling-fix - .class_image - img.img-responsive(src="/images/pages/contribute/artisan.png", alt="") + a(href="/contribute/diplomat") - h3.header-scrolling-fix - span(data-i18n="classes.artisan_title") Artisan - span - span(data-i18n="classes.artisan_title_description") (Level Builder) - p - span(data-i18n="contribute.artisan_summary_pref") - | Want to design levels and expand CodeCombat's arsenal? People are playing through our - | content at a pace faster than we can build! Right now, our level editor is barebone, - | so be wary. Making levels will be a little challenging and buggy. If you have visions - | of campaigns spanning for-loops to - span - a(href="http://stackoverflow.com/questions/758088/seeking-contrived-example-code-continuations/758105#758105") - | Mondo Bizzaro - span(data-i18n="contribute.artisan_summary_suf") - | , then this class is for you. + div.class_tile + img.tile-img(src="/images/pages/contribute/tile_diplomat.png", alt="") - a(href="/contribute/artisan") - p.lead(data-i18n="contribute.more_about_artisan") - | Learn More About Becoming An Artisan + div.class_text + h3 + span.spr(data-i18n="classes.diplomat_title") Diplomat + span(data-i18n="classes.diplomat_title_description") - .contributor-signup(data-contributor-class-id="level_creator", data-contributor-class-name="artisan") + p(data-i18n="classes.diplomat_summary") + | CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations. - #adventurer.header-scrolling-fix + a(href="/contribute/ambassador") + div.class_tile + img.tile-img(src="/images/pages/contribute/tile_ambassador.png", alt="") - .class_image - img.img-responsive(src="/images/pages/contribute/adventurer.png", alt="") + div.class_text + h3 + span.spr(data-i18n="classes.ambassador_title") Ambassador + span(data-i18n="classes.ambassador_title_description") - h3.header-scrolling-fix - span(data-i18n="classes.adventurer_title") Adventurer - span - span(data-i18n="classes.adventurer_title_description") (Level Playtester) - p(data-i18n="contribute.adventurer_summary") - | Let us be clear about your role: you are the tank. You are going to take heavy damage. - | We need people to try out brand-new levels and help identify how to make things better. - | The pain will be enormous; making good games is a long process and no one gets - | it right the first time. - | If you can endure and have a high constitution score, then this class is for you. - - a(href="/contribute/adventurer") - p.lead(data-i18n="contribute.more_about_adventurer") - | Learn More About Becoming an Adventurer + p(data-i18n="classes.ambassador_summary") + | Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world. - .contributor-signup(data-contributor-class-id="tester", data-contributor-class-name="adventurer") - - #scribe.header-scrolling-fix - - .class_image - img.img-responsive(src="/images/pages/contribute/scribe.png", alt="") - - h3.header-scrolling-fix - span(data-i18n="classes.scribe_title") Scribe - span - span(data-i18n="classes.scribe_title_description") (Article Editor) - p - span(data-i18n="contribute.scribe_summary_pref") - | CodeCombat is not just going to be a bunch of levels. It will also be a resource of - | programming knowledge that players can hook into. That way, each Artisan can link - | to a detailed article that for the player's edification: - | documentation akin to what the - a(href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide", data-i18n="contribute.scribe_introduction_url_mozilla") - | Mozilla Developer Network - span(data-i18n="contribute.scribe_summary_suf") - | has built. If you enjoy explaining programming concepts, then this class is for you. - - a(href="/contribute/scribe") - p.lead(data-i18n="contribute.more_about_scribe") - | Learn More About Becoming a Scribe - - .contributor-signup(data-contributor-class-id="article_editor", data-contributor-class-name="scribe") - - #diplomat.header-scrolling-fix - - .class_image - img.img-responsive(src="/images/pages/contribute/diplomat.png", alt="") - - h3.header-scrolling-fix - span(data-i18n="classes.diplomat_title") Diplomat - span - span(data-i18n="classes.diplomat_title_description") (Translator) - p - span(data-i18n="contribute.diplomat_summary") - | There is a large interest in CodeCombat in other countries that do not speak English! - | We are looking for translators who are willing to spend their time translating the - | site's corpus of words so that CodeCombat is accessible across the world as soon as - | possible. If you'd like to help getting CodeCombat international, then this class is - | for you. - - a(href="/contribute/diplomat") - p.lead(data-i18n="contribute.more_about_diplomat") - | Learn More About Becoming a Diplomat - - .contributor-signup(data-contributor-class-id="translator", data-contributor-class-name="diplomat") - - #ambassador.header-scrolling-fix - - .class_image - img.img-responsive(src="/images/pages/contribute/ambassador.png", alt="") - - h3.header-scrolling-fix - span(data-i18n="classes.ambassador_title") Ambassador - span - span(data-i18n="classes.ambassador_title_description") (Support) - p(data-i18n="contribute.ambassador_summary") - | We are trying to build a community, and every community needs a support team when - | there are troubles. We have got chats, emails, and social networks so that our users - | can get acquainted with the game. If you want to help people get involved, have fun, - | and learn some programming, then this c lass is for you. - - a(href="/contribute/ambassador") - p.lead(data-i18n="contribute.more_about_ambassador") - | Learn More About Becoming an Ambassador - - .contributor-signup(data-contributor-class-id="support", data-contributor-class-name="ambassador") - - div.clearfix \ No newline at end of file + div.clearfix \ No newline at end of file diff --git a/app/templates/contribute/contribute_nav.jade b/app/templates/contribute/contribute_nav.jade deleted file mode 100644 index 90b07133e..000000000 --- a/app/templates/contribute/contribute_nav.jade +++ /dev/null @@ -1,33 +0,0 @@ -ul.contribute_class.affix.nav.nav-list.nav-pills#contribute-nav - li - h3(data-i18n="contribute.character_classes_title") Character Classes - li - a(href=navPrefix + "#archmage") - span(data-i18n="classes.archmage_title") Archmage - span - span(data-i18n="classes.archmage_title_description") (Coder) - li - a(href=navPrefix + "#artisan") - span(data-i18n="classes.artisan_title") Artisan - span - span(data-i18n="classes.artisan_title_description") (Level Builder) - li - a(href=navPrefix + "#adventurer") - span(data-i18n="classes.adventurer_title") Adventurer - span - span(data-i18n="classes.adventurer_title_description") (Level Playtester) - li - a(href=navPrefix + "#scribe") - span(data-i18n="classes.scribe_title") Scribe - span - span(data-i18n="classes.scribe_title_description") (Article Editor) - li - a(href=navPrefix + "#diplomat") - span(data-i18n="classes.diplomat_title") Diplomat - span - span(data-i18n="classes.diplomat_title_description") (Translator) - li - a(href=navPrefix + "#ambassador") - span(data-i18n="classes.ambassador_title") Ambassador - span - span(data-i18n="classes.ambassador_title_description") (Support) \ No newline at end of file diff --git a/app/templates/contribute/contributor_signup_anonymous.jade b/app/templates/contribute/contributor_signup_anonymous.jade index 4fc05a4bb..ae5263dc6 100644 --- a/app/templates/contribute/contributor_signup_anonymous.jade +++ b/app/templates/contribute/contributor_signup_anonymous.jade @@ -7,7 +7,7 @@ if me.attributes.anonymous | To subscribe for class emails, you'll need to be logged in first. strong.spl - a.auth-button + a.signup-button span(data-i18n="login.log_in") Log In span.spr.spl / span(data-i18n="login.sign_up") Create Account \ No newline at end of file diff --git a/app/templates/contribute/diplomat.jade b/app/templates/contribute/diplomat.jade index be8183a5e..f46317cde 100644 --- a/app/templates/contribute/diplomat.jade +++ b/app/templates/contribute/diplomat.jade @@ -4,12 +4,11 @@ block content div.contribute_class - include /templates/contribute/contribute_nav + .class_detail + + img(src="/images/pages/contribute/class_detail_diplomat.png", alt="") div.class-main#diplomat-main - - .class_image - img.img-responsive(src="/images/pages/contribute/diplomat.png", alt="") h2 span(data-i18n="classes.diplomat_title") Diplomat @@ -18,7 +17,7 @@ block content p span(data-i18n="contribute.diplomat_introduction_pref") | So, if there's one thing we learned from the - a(href="blog.codecombat.com/post/64658141307/codecombat-in-y-combinator", data-i18n="contribute.diplomat_launch_url") + a(href="http://blog.codecombat.com/post/64658141307/codecombat-in-y-combinator", data-i18n="contribute.diplomat_launch_url") | launch in October span , span(data-i18n="contribute.diplomat_introduction_suf") @@ -71,6 +70,6 @@ block content span.spl - #{stats.diplomats.join(', ')} .progress .progress-bar(style='width: ' + (100 * stats.completion) + '%') - span(style=stats.completion < 0.06 ? 'color: black; text-shadow: 0px 1px 0px white' : '')= (100 * stats.completion).toFixed(1) + '%' + span(style=stats.completion < 0.06 ? 'color: black; text-shadow: 0px 1px 0px white' : '')= Math.min(100, (100 * stats.completion).toFixed(1)) + '%' div.clearfix diff --git a/app/templates/contribute/scribe.jade b/app/templates/contribute/scribe.jade index 6e752c421..432646ba0 100644 --- a/app/templates/contribute/scribe.jade +++ b/app/templates/contribute/scribe.jade @@ -4,13 +4,12 @@ block content div.contribute_class - include /templates/contribute/contribute_nav + .class_detail + + img(src="/images/pages/contribute/class_detail_scribe.png", alt="") div.class-main#scribe-main - .class_image - img.img-responsive(src="/images/pages/contribute/scribe.png", alt="") - h2 span(data-i18n="classes.scribe_title") Scribe span @@ -37,7 +36,7 @@ block content h4(data-i18n="contribute.how_to_join") How To Join p - a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="contribute.contact_us_url") + a(tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="contribute.contact_us_url") | Contact us span , span(data-i18n="contribute.scribe_join_description") diff --git a/app/templates/achievements/achievement-popup.jade b/app/templates/core/achievement-popup.jade similarity index 100% rename from app/templates/achievements/achievement-popup.jade rename to app/templates/core/achievement-popup.jade diff --git a/app/templates/modal/auth-modal-gplus-checklist.jade b/app/templates/core/auth-modal-gplus-checklist.jade similarity index 100% rename from app/templates/modal/auth-modal-gplus-checklist.jade rename to app/templates/core/auth-modal-gplus-checklist.jade diff --git a/app/templates/core/auth.jade b/app/templates/core/auth.jade index fffa44ad1..3f5ff3221 100644 --- a/app/templates/core/auth.jade +++ b/app/templates/core/auth.jade @@ -1,84 +1,96 @@ -extends /templates/modal/modal_base - -block modal-header-content - if mode === 'login' - h3(data-i18n="login.log_in") Log In - if mode === 'signup' - if title === 'short' - h3(data-i18n="login.sign_up") Create Account - else - h3(data-i18n="signup.create_account_title") Create Account to Save Progress - -block modal-body-content - - if showRequiredError - .alert.alert-success - span(data-i18n="signup.required") You need to log in before you can that way. - - else if mode === 'signup' && descriptionOn === "yes" - p(data-i18n="signup.description") It's free. Just need a couple things and you'll be good to go: - - form.form - if onEmployersPage - .form-group - input#email.input-large.form-control(name="email", type="email", value=formValues.email, placeholder="Email") - .form-group - input#password.input-large.form-control(name="password", type="password", value=formValues.password, placeholder="Password") - - else - .form-group - label.control-label(for="email", data-i18n="general.email") Email - input#email.input-large.form-control(name="email", type="email", value=formValues.email) - .form-group - label.control-label(for="password", data-i18n="general.password") Password - input#password.input-large.form-control(name="password", type="password", value=formValues.password) - - if mode === 'signup' - .form-group - label.control-label(for="name", data-i18n="general.name") Name - if me.get('name') - input#name.input-large.form-control(name="name", type="text", value="#{me.get('name')}") - else - input#name.input-large.form-control(name="name", type="text", value="", placeholder="Anoner") - .form-group.checkbox - label.control-label(for="subscribe") - input#subscribe(name="subscribe", type="checkbox", checked='checked') - span(data-i18n="signup.email_announcements") Receive announcements by email - .form-group.checkbox - label.control-label(for="confirm-age") - input#confirm-age(name="confirm-age", type="checkbox", checked='checked') - span(data-i18n="signup.coppa") 13+ or non-USA - a(href="https://en.wikipedia.org/wiki/Children's_Online_Privacy_Protection_Act", data-i18n="signup.coppa_why", target="_blank") (Why?) +.modal-dialog + .modal-content if mode === 'login' - if onEmployersPage - input.btn.btn-info.btn-large#login-button(value=translate("login.log_in"), type="submit") - else - input.btn.btn-info.btn-large#login-button(value=translate("login.log_in"), type="submit") - .btn.btn-default.btn-large#switch-to-signup-button(data-i18n="login.sign_up") Create Account - else if mode === 'signup' - input.btn.btn-info.btn-large#signup-button(value=translate("signup.sign_up"), type="submit") - .btn.btn-default.btn-large#switch-to-login-button(data-i18n="login.log_in") + img(src="/images/pages/modal/auth/login-background.png", draggable="false").auth-modal-background + else + img(src="/images/pages/modal/auth/signup-background.png", draggable="false").auth-modal-background + + if mode === 'login' + h1(data-i18n="login.log_in") Log In + if mode === 'signup' + h1(data-i18n="login.sign_up") Create Account + + div#close-modal + span.glyphicon.glyphicon-remove -block modal-body-wait-content - - if mode === 'login' - h3(data-i18n="login.logging_in") Logging In - if mode === 'signup' - h3(data-i18n="signup.creating") Creating Account... + .auth-form-content -block modal-footer - .modal-footer - div.network-login - btn.btn.btn-sm.github-login-button#github-login-button - img(src="/images/pages/modal/auth/github_icon.png") - | GitHub - div.network-login - .fb-login-button(data-show-faces="false", data-width="200", data-max-rows="1", data-scope="email") - // Google+ login causing script errors on IE10 - if !isIE - div.network-login + if showRequiredError + .alert.alert-success + span(data-i18n="signup.required") You need to log in before you can that way. + else if mode === 'signup' && showSignupRationale + .alert.alert-info + span(data-i18n="play_level.victory_sign_up_poke") Want to save your code? Create a free account! + + form.form + .form-group + label.control-label(for="email") + span(data-i18n="general.email") Email + | : + .input-border + input#email.input-large.form-control(name="email", type="email", value=formValues.email) + .form-group + if mode === 'login' + div#recover-account-wrapper + a(data-toggle="coco-modal", data-target="core/RecoverModal", data-i18n="login.forgot_password")#link-to-recover Forgot your password? + label.control-label(for="password") + span(data-i18n="general.password") Password + | : + .input-border + input#password.input-large.form-control(name="password", type="password", value=formValues.password) + + if mode === 'signup' + .form-group + label.control-label(for="name") + span(data-i18n="general.name") Name + | : + .input-border + if me.get('name') + input#name.input-large.form-control(name="name", type="text", value="#{me.get('name')}") + else + input#name.input-large.form-control(name="name", type="text", value="", placeholder="Anoner") + .form-group.checkbox + label.control-label(for="subscribe") + .input-border + input#subscribe(name="subscribe", type="checkbox", checked='checked') + span.custom-checkbox + .glyphicon.glyphicon-ok + span(data-i18n="signup.email_announcements") Receive announcements by email + + if mode === 'login' + input.btn.btn-lg.btn-illustrated.btn-block#login-button(value=translate("login.log_in"), type="submit") + else if mode === 'signup' + input.btn.btn-lg.btn-illustrated.btn-block#signup-button(value=translate("signup.sign_up"), type="submit") + + .wait.secret + if mode === 'login' + h3(data-i18n="login.logging_in") Logging In + if mode === 'signup' + h3(data-i18n="signup.creating") Creating Account... + + .auth-network-logins + // GitHub login too buggy to survive + //div.network-login + // btn.btn.btn-sm.github-login-button#github-login-button + // img(src="/images/pages/modal/auth/github_icon.png") + // | GitHub + .btn.btn-primary.btn-lg.btn-illustrated.network-login + img.network-logo(src="/images/pages/community/logo_facebook.png", draggable="false") + span.sign-in-blurb(data-i18n="login.sign_in_with_facebook") Sign in with Facebook + .facebook-login-wrapper + .fb-login-button(data-show-faces="false", data-width="200", data-max-rows="1", data-scope="email") + .btn.btn-danger.btn-lg.btn-illustrated.network-login + img.network-logo(src="/images/pages/community/logo_g+.png", draggable="false") + span.sign-in-blurb(data-i18n="login.sign_in_with_gplus") Sign in with G+ + .gplus-login-wrapper .gplus-login-button#gplus-login-button - div#recover-account-wrapper - a(data-toggle="coco-modal", data-target="modal/RecoverModal", data-i18n="login.recover")#link-to-recover recover account + + .extra-pane + if mode === 'login' + .switch-explanation(data-i18n="login.signup_switch") Want to create an account? + .btn.btn-default.btn-lg.btn-illustrated#switch-to-signup-button(data-i18n="login.sign_up") Create Account + else if mode === 'signup' + .switch-explanation(data-i18n="signup.login_switch") Already have an account? + .btn.btn-default.btn-lg.btn-illustrated#switch-to-login-button(data-i18n="login.log_in") diff --git a/app/templates/core/contact.jade b/app/templates/core/contact.jade new file mode 100644 index 000000000..d57702fb3 --- /dev/null +++ b/app/templates/core/contact.jade @@ -0,0 +1,38 @@ +extends /templates/core/modal-base + +block modal-header-content + h3(data-i18n="contact.contact_us") Contact CodeCombat... + +block modal-body-content + p + span(data-i18n="contact.welcome") Good to hear from you! Use this form to send us email. + span.spl(data-i18n="contact.forum_prefix") For anything public, please try + a(href="http://discourse.codecombat.com/", data-i18n="contact.forum_page") our forum + span(data-i18n="contact.forum_suffix") instead. + span.spl.spr(data-i18n="contact.faq_prefix") There's also a + a(data-i18n="contact.faq", href="http://discourse.codecombat.com/t/faq-check-before-posting/1027") FAQ + | . + if me.isPremium() + p(data-i18n="contact.subscriber_support") Since you're a CodeCombat subscriber, your email will get our priority support. + else + p + span.spr(data-i18n="contact.subscribe_prefix") If you need help figuring out a level, please + a(data-toggle="coco-modal", data-target="core/SubscribeModal", data-i18n="contact.subscribe") buy a CodeCombat subscription + span.spl(data-i18n="contact.subscribe_suffix") and we'll be happy to help you with your code. + .form + .form-group + label.control-label(for="contact-email", data-i18n="general.email") Email + input#contact-email.form-control(name="email", type="email", value="#{me.get('anonymous') ? '' : me.get('email')}", data-i18n="[placeholder]contact.where_reply", placeholder="Where should we reply?") + .form-group + label.control-label(for="contact-message", data-i18n="general.message") Message + textarea#contact-message.form-control(name="message", rows=8) + + #contact-screenshot.secret + a(target='_blank', data-i18n="contact.screenshot_included") Screenshot included. + br + img.pull-left(width=100) + +block modal-footer-content + span.sending-indicator.pull-left.secret(data-i18n="common.sending") Sending... + a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="common.cancel").btn Cancel + button.btn.btn-primary#contact-submit-button(data-i18n="contact.send") Send Feedback diff --git a/app/templates/modal/diplomat_suggestion.jade b/app/templates/core/diplomat-suggestion.jade similarity index 90% rename from app/templates/modal/diplomat_suggestion.jade rename to app/templates/core/diplomat-suggestion.jade index 56d98f33a..2faddfa11 100644 --- a/app/templates/modal/diplomat_suggestion.jade +++ b/app/templates/core/diplomat-suggestion.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="diplomat_suggestion.title") Help translate CodeCombat! @@ -11,7 +11,7 @@ block modal-body-content p(data-i18n="diplomat_suggestion.missing_translations") Until we can translate everything into {English}, you'll see English when {English} isn't available. p - a(href="/contribute#diplomat", data-i18n="diplomat_suggestion.learn_more") Learn more about being a Diplomat + a(href="/contribute/diplomat", data-i18n="diplomat_suggestion.learn_more") Learn more about being a Diplomat block modal-footer-content button.btn.btn-primary.btn-large#subscribe-button(data-i18n="diplomat_suggestion.subscribe_as_diplomat") Subscribe as a Diplomat diff --git a/app/templates/modal/error.jade b/app/templates/core/error.jade similarity index 100% rename from app/templates/modal/error.jade rename to app/templates/core/error.jade diff --git a/app/templates/loading_error.jade b/app/templates/core/loading-error.jade similarity index 91% rename from app/templates/loading_error.jade rename to app/templates/core/loading-error.jade index fdd566a45..d0d2ac5ca 100644 --- a/app/templates/loading_error.jade +++ b/app/templates/core/loading-error.jade @@ -25,4 +25,4 @@ strong(data-i18n="loading_error.unknown") Unknown error. button.btn.btn-xs.retry-loading-resource(data-i18n="common.retry", data-resource-index=resourceIndex) Retry - button.btn.btn-xs.skip-loading-resource(data-i18n="common.skip", data-resource-index=resourceIndex) Skip + button.btn.btn-xs.skip-loading-resource(data-i18n="play_level.skip", data-resource-index=resourceIndex) Skip diff --git a/app/templates/loading.jade b/app/templates/core/loading.jade similarity index 100% rename from app/templates/loading.jade rename to app/templates/core/loading.jade diff --git a/app/templates/modal/modal_base.jade b/app/templates/core/modal-base.jade similarity index 100% rename from app/templates/modal/modal_base.jade rename to app/templates/core/modal-base.jade diff --git a/app/templates/not_found.jade b/app/templates/core/not-found.jade similarity index 79% rename from app/templates/not_found.jade rename to app/templates/core/not-found.jade index bc2d32d13..77bb47af2 100644 --- a/app/templates/not_found.jade +++ b/app/templates/core/not-found.jade @@ -4,7 +4,7 @@ block content h1.text-center(data-i18n="not_found.page_not_found") Page Not Found - num = Math.floor(Math.random() * 3) + 1 + - var num = Math.floor(Math.random() * 3) + 1; img(src="/images/pages/not_found/404_#{num}.png" class="not-found-image") diff --git a/app/templates/modal/recover.jade b/app/templates/core/recover-modal.jade similarity index 93% rename from app/templates/modal/recover.jade rename to app/templates/core/recover-modal.jade index 76c576f92..1d5930680 100644 --- a/app/templates/modal/recover.jade +++ b/app/templates/core/recover-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="recover.recover_account_title") Recover Account diff --git a/app/templates/core/subscribe-modal.jade b/app/templates/core/subscribe-modal.jade new file mode 100644 index 000000000..724ebc7c0 --- /dev/null +++ b/app/templates/core/subscribe-modal.jade @@ -0,0 +1,110 @@ +.modal-dialog + .modal-content + if state === 'purchasing' + .alert.alert-info(data-i18n="buy_gems.purchasing") + + else if state === 'retrying' + #retrying-alert.alert.alert-danger(data-i18n="buy_gems.retrying") + + else + img#subscribe-background(src="/images/pages/play/modal/subscribe-background-blank.png") + img.subscribe-image(src="/images/pages/play/modal/subscribe-heroes.png") + + h1(data-i18n="subscribe.subscribe_title") Subscribe + + div#close-modal + span.glyphicon.glyphicon-remove + + div.comparison-blurb(data-i18n="subscribe.comparison_blurb") + table.table.table-condensed.table-bordered.comparison-table + thead + tr + th + if !me.get('chinaVersion') + th.free-cell(data-i18n="subscribe.free") + th + //- TODO: find a better way to localize '$9.99/month' + span $#{price}/ + span(data-i18n="subscribe.month") + tbody + tr + td.feature-description + span(data-i18n="subscribe.feature1") + if !me.get('chinaVersion') + td.center-ok.free-cell + span.glyphicon.glyphicon-ok + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="[html]subscribe.feature2") + if !me.get('chinaVersion') + td.free-cell + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="subscribe.feature3") + if !me.get('chinaVersion') + td.free-cell + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="[html]subscribe.feature4") + if !me.get('chinaVersion') + td.free-cell + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="subscribe.feature5") + if !me.get('chinaVersion') + td.free-cell + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="subscribe.feature6") + if !me.get('chinaVersion') + td.free-cell + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="[html]subscribe.feature7") + if !me.get('chinaVersion') + td.free-cell + td.center-ok + span.glyphicon.glyphicon-ok + #parents-info(data-i18n="subscribe.parents") + #payment-methods-info(data-i18n="subscribe.payment_methods") + + button.btn.btn-lg.btn-illustrated.purchase-button(data-i18n="subscribe.subscribe_title") + button.btn.btn-lg.btn-illustrated.parent-button(data-i18n="subscribe.parent_button") + + if state === 'declined' + #declined-alert.alert.alert-danger.alert-dismissible + span(data-i18n="buy_gems.declined") + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + + if state === 'unknown_error' + #error-alert.alert.alert-danger.alert-dismissible + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + p(data-i18n="loading_error.unknown") + p= stateMessage + + .parent-button-popover-content.hidden + .email-parent-form + p(data-i18n="subscribe.parent_email_description") + form + .form-group + label(data-i18n="subscribe.parent_email_input_label") + input.parent-input.form-control(type='email', data-i18n="[placeholder]subscribe.parent_email_input_placeholder") + .parent-email-validator.email_invalid(data-i18n="subscribe.parent_email_input_invalid") + button.parent-send.btn.btn-default(type='submit', data-i18n="subscribe.parent_email_send") + .email-parent-complete + p(data-i18n="subscribe.parent_email_sent") + button.btn(type='button', onclick="$('.parent-button').popover('hide');", data-i18n="modal.close") diff --git a/app/templates/courses/mock1/course-details.jade b/app/templates/courses/mock1/course-details.jade new file mode 100644 index 000000000..5c2a01c65 --- /dev/null +++ b/app/templates/courses/mock1/course-details.jade @@ -0,0 +1,213 @@ +extends /templates/base + +block content + + //- DO NOT localize / i18n + + div TODO: fix ugly tabs + div + span *UNDER CONSTRUCTION, send feedback to + a.spl(href='mailto:team@codecombat.com') team@codecombat.com + div + input.student-mode-checkbox(type='checkbox', checked=studentMode) + span.spl Student view + div(style='border-bottom: 1px solid black;') + + .modal#editSettingsModal + .modal-dialog + .modal-header + button.close(data-dismiss='modal') + span × + h3.modal-title Edit Class Settings + .modal-body + p This title will be displayed to everyone in the class. + p + input.edit-name-input(type='text', value="#{instance.name}") + p This description will be displayed to everyone in the class. + p + textarea.edit-description-input(rows=2)= instance.description + p Select programming languages available to the class: + p + select.form-control.select-language + option(value="Python") Python + option(value="JavaScript") JavaScript + option(value="All Languages") All Languages + p + input(type='checkbox', checked) + span.spl Show student progress to everyone in the class + .modal-footer + button.btn.btn-save-settings(data-i18n="common.save_changes") + + h1= instance.name + small.spl (#{course.title}) + + p + if instance.description + each line in instance.description.split('\n') + div= line + + if !studentMode + p + button.btn.btn-xs.btn-edit-settings(data-toggle='modal', data-target='#editSettingsModal') edit class settings + + div(role='tabpanel') + ul.nav.nav-tabs(role='tablist') + if studentMode + li.active(role='presentation') + a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels + li(role='presentation') + a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab') Class + else + li.active(role='presentation') + a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab') Class + li(role='presentation') + a(href='#invite', aria-controls='invite', role='tab', data-toggle='tab') Add Students + li(role='presentation') + a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels + + .tab-content + if studentMode + .tab-pane.active#levels(role='tabpanel') + +levels-tab + .tab-pane#progress(role='tabpanel') + +progress-tab + else + .tab-pane.active#progress(role='tabpanel') + +progress-tab + .tab-pane#invite(role='tabpanel') + br + p Invite students to join this class. + if course.title !== 'Introduction to Computer Science' + p Student unlock code: #{instance.code} + p Class capacity: 34/50 + textarea.textarea-emails(rows=3, placeholder="Enter student emails to invite, one per line") + div(style='margin-top:10px;') + button.btn.btn-success.btn-invite Send Invites + .tab-pane#levels(role='tabpanel') + +levels-tab + +mixin progress-tab + if instance.students + .container-fluid.summary-container + .row + .col-md-6 + h3 Statistics + table.statistics-container + tr + td Total students: + td #{instance.students.length} + tr + td Average level play time: + td #{stats.averageLevelPlaytime} seconds + tr + td Total play time: + td #{stats.totalPlayTime} seconds + tr + td Average levels completed: + td #{stats.averageLevelsCompleted} + tr + td Total levels completed: + td #{stats.totalLevelsCompleted} + tr + td Last level completed: + td #{stats.lastLevelCompleted} + .col-md-6 + h3 Concepts Covered + table.table-concepts-summary + each concept in courseConcepts + - var conceptCompletion = Math.round(parseFloat(conceptsCompleted[concept]) / instance.students.length * 100) + if isNaN(conceptCompletion) + - conceptCompletion = 0 + tr + td.concept-completion-container + span.concept-summary(style="width:#{conceptCompletion}%;") + span.concept-completed-foreground(data-i18n="concepts." + concept) + span.spl - #{conceptCompletion}% + + h3 Students + table.table.table-condensed + thead + tr + th + span.member-header.spr Name + if memberSort === 'nameAsc' + span.member-header.glyphicon.glyphicon-chevron-up + else if memberSort === 'nameDesc' + span.member-header.glyphicon.glyphicon-chevron-down + th + span.progress-header.spr Progress + if memberSort === 'progressAsc' + span.progress-header.glyphicon.glyphicon-chevron-up + else if memberSort === 'progressDesc' + span.progress-header.glyphicon.glyphicon-chevron-down + else + span(style='padding-left:16px;') + span.progress-key.progress-key-complete complete + span.progress-key.progress-key-started started + span.progress-key not started + if maxLastStartedIndex > 30 + input.expand-progress-checkbox(type='checkbox') + span.spl.expand-progress-label(data-i18n="clans.exp_levels") Expand levels + tbody + each student in instance.students + tr + td + a= student + td.progress-cell + .level-progression-concepts Concepts + each concept in courseConcepts + if userConceptsMap[student] && userConceptsMap[student][concept] === 'complete' + span.spr.progress-concept-cell.progress-concept-cell-complete(data-i18n="concepts." + concept) + else if userConceptsMap[student] && userConceptsMap[student][concept] === 'started' + span.spr.progress-concept-cell.progress-concept-cell-started(data-i18n="concepts." + concept) + else + span.spr.progress-concept-cell.progress-concept-cell-not-started(data-i18n="concepts." + concept) + + .level-progression-levels Levels + - var i = 0 + each level in course.levels + if userLevelStateMap[student][level] === 'complete' + span.progress-level-cell.progress-level-cell-complete #{i + 1} + if showExpandedProgress || i === 0 || i === course.levels.length - 1 + span.spl #{level} + .level-popup-container + h3 #{i + 1}. #{level} + p + div + - var playTime = Math.round(Math.random() * 600) + span Time to solve + span : #{playTime} seconds + div + - var completionDate = new Date() + - completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60)) + span Completed on + span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')} + strong(data-i18n="clans.view_solution") Click to view solution. + else if userLevelStateMap[student][level] === 'started' + span.progress-level-cell.progress-level-cell-started #{i + 1} #{level} + else + span.progress-level-cell.level-progression-level-not-started #{i + 1} + if showExpandedProgress || i === 0 + span.spl #{level} + - i++ + +mixin levels-tab + table.table.table-striped.table-condensed + thead + tr + th + th Status + th Level + th Concepts + tbody + - var student = instance.students[0] + each level in course.levels + tr + td + button.btn.btn-success.btn-play-level(data-level=level) Play + td= userLevelStateMap[student][level] + td= level + td + each concept in courseConcepts + if levelConceptsMap[level] && levelConceptsMap[level][concept] + span.spr.progress-level-cell.level-progression-level-not-started(data-i18n="concepts." + concept) diff --git a/app/templates/courses/mock1/course-enroll.jade b/app/templates/courses/mock1/course-enroll.jade new file mode 100644 index 000000000..5853081e5 --- /dev/null +++ b/app/templates/courses/mock1/course-enroll.jade @@ -0,0 +1,53 @@ +extends /templates/base + +block content + + //- DO NOT localize / i18n + + div + span *UNDER CONSTRUCTION, send feedback to + a.spl(href='mailto:team@codecombat.com') team@codecombat.com + div(style='border-bottom: 1px solid black') + + .well.well-lg.enroll-container + h1.center Buy Course + h3 1. Course + p Select 'All Courses' for a 50% discount! + .form-group + select.form-control.course-select + each course in courses + option(value="#{course.title}")= course.title + option(value="All Courses") All Courses + + h3 2. Number of students + p Enter the number of students you need for this class. + input.input-quantity(type='text', value="#{quantity}") + + h3 3. Name your class + p This will be displayed on the course page for you and your students, and it can be changed later. + input.session-name(type='text', placeholder="Mrs. Smith's 4th Period") + + h3 4. Buy + p + span.spr You are purchasing a license for + strong.spr #{selectedCourseTitle} + span.spr for + strong #{quantity} students + | . + p After purchase you will receive an unlock code to distribute to your students, which they can use to enroll in your class. + p.center + if price > 0 + button.btn.btn-success.btn-lg.btn-buy $#{price} + else + button.btn.btn-success.btn-lg.btn-buy FREE + + h3 Free trial for teachers! + p + span.spr Please fill out our + a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2") + span.spl to get individual access to all courses for evalutaion purposes. + + h3 Questions? + p + span Please contact + a.spl(href='mailto:team@codecombat.com') team@codecombat.com diff --git a/app/templates/courses/mock1/course-info.jade b/app/templates/courses/mock1/course-info.jade new file mode 100644 index 000000000..ffecb75df --- /dev/null +++ b/app/templates/courses/mock1/course-info.jade @@ -0,0 +1,78 @@ +extends /templates/base + +block content + + //- DO NOT localize / i18n + + div + span *UNDER CONSTRUCTION, send feedback to + a.spl(href='mailto:team@codecombat.com') team@codecombat.com + div(style='border-bottom: 1px solid black; margin-bottom: 20px') + + + .info-container + h1.center= course.title + p.center.gameplay-img-container + img(src="#{course.image}" width='800') + p= course.description + + h1.center Learn More in Less Time + p + span.spr Your students will learn + strong.spr more computer science + span.spr material in + strong.spr less time + span with CodeCombat! + p + span.spr In about + strong.spr #{course.duration} hours + span.spr your students will work through + strong.spr #{course.levels.length} lessons + span and cover these high-level topics: + p + ul + each topic in course.topics + li= topic + + h1.center No Experience Necesssary + p + strong.spr No outside experience + span is needed for students to complete this course. They will pick up where the left off from the previous CodeCombat course. + p + strong.spr Teachers do not need programming experience + span to administer this course. Your students will learn on their own. + p + span.spr There are built-in + strong.spr help videos, level guides, tool tips + span to explain everything to your students. + p + span.spr Use + strong.spr student progress monitoring + span to match up stronger students with those that need a little extra help! + + h1.center Monitor Student Progress + p.center.monitoring-img-container + img(src='/images/pages/clans/dashboard_preview.png' width='700') + div.center.caption-text TODO: Add caption + p.progress-container + ul + li + strong Track concepts + span.spl learned by each student + li Track levels completed for each student + li + span See your students' + strong.spl solutions + li Sort students by name or progress + + h1.center Teachers Love CodeCombat! + p + div.praise-quote "#{praise.quote}" + div.caption-text - #{praise.source} + + p.center + button.btn.btn-info.btn-lg.btn-enroll(data-course-id="#{courseID}") Enroll Now + + p.contact-container + span For more information, please contact + a.spl(href='mailto:team@codecombat.com') team@codecombat.com diff --git a/app/templates/courses/mock1/courses.jade b/app/templates/courses/mock1/courses.jade new file mode 100644 index 000000000..986dbe928 --- /dev/null +++ b/app/templates/courses/mock1/courses.jade @@ -0,0 +1,102 @@ +extends /templates/base + +block content + + //- DO NOT localize / i18n + + div TODO: Add already enrolled view checkbox + div(style='border-bottom: 1px solid black') + span *UNDER CONSTRUCTION, send feedback to + a.spl(href='mailto:team@codecombat.com') team@codecombat.com + + .modal#continueModal + .modal-dialog + .modal-header + button.close(data-dismiss='modal') + span × + h3.modal-title Loading... + .modal-body + .container-fluid + .row.button-row.row-pick-class + .col-md-12 + .well.well-sm + p + div.instruction-label Pick your class + .container-fluid + .row + .col-md-8 + select.form-control.select-session + each inst in instances + option= inst.name + .col-md-4 + button.btn.btn-success.btn-enter(data-course-id="#{courseID}") Enter + .row.button-row.center.row-pick-class + .col-md-12 + div.or Or + .row.button-row + .col-md-12 + .well.well-sm + p + div.instruction-label Enter an unlock code + .container-fluid + .row + .col-md-8 + input.code-input(type='text', placeholder="Enter unlock code") + .col-md-4 + button.btn.btn-success.btn-enroll(data-course-id="#{courseID}") Enroll + .row.button-row.center + .col-md-12 + div.or Or + .row.button-row.center + .col-md-12 + button.btn.btn-success.btn-lg.btn-buy(data-course-id="#{courseID}") Buy this course + + h1.center Courses on CodeCombat + + .info-container + p Courses are designed to introduce computer science concepts using CodeCombat's fun and engaging environment. CodeCombat levels are organized around key topics to encourage progressive learning, over the course of 5 hours. + + .container-fluid + .row + .col-md-6 + ul + li Learn more in less time + li No coding experience necesssary + li Easily monitor student progress + + div Purchase a course for your entire class. It's easy to sign up your students! + .col-md-6 + .well.well-sm + div.praise-quote "#{praise.quote}" + div.caption-text - #{praise.source} + + h2.center Choose Your Course: + + .container-fluid + - var i = 0 + while i < courses.length + .row + +course-block(courses[i], i) + - i++ + if i < courses.length + +course-block(courses[i], i) + - i++ + +mixin course-block(course, courseID) + .col-md-6 + .well.panel.course-panel(class=course.unlocked ? 'panel-success' : 'panel-info') + .panel-heading + .panel-title + span.spr #{course.title} + strong #{course.unlocked ? '[ enrolled ]' : ''} + .panel-body + .container-fluid + .row.button-row + .col-md-6 + strong Topics + ul + each topic in course.topics + li= topic + strong Hours of content: #{course.duration} + .col-md-6.center + button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal", data-course-title="#{course.title}", data-course-id="#{courseID}") #{course.unlocked ? 'Continue' : 'Enter'} diff --git a/app/templates/editor/achievement/edit.jade b/app/templates/editor/achievement/edit.jade index 8e811b30c..4de836b50 100644 --- a/app/templates/editor/achievement/edit.jade +++ b/app/templates/editor/achievement/edit.jade @@ -1,7 +1,7 @@ extends /templates/base block content - if me.isAdmin() + if !unauthorized ol.breadcrumb li a(href="/editor", data-i18n="editor.main_title") CodeCombat Editors @@ -13,7 +13,7 @@ block content button.achievement-tool-button(data-i18n="", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#recalculate-all-button Recalculate All button.achievement-tool-button(data-i18n="", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#recalculate-button Recalculate button.achievement-tool-button(data-i18n="common.delete", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#delete-button Delete - button.achievement-tool-button(data-i18n="common.save", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#save-button Save + button.achievement-tool-button(data-i18n="common.save", disabled=(me.isAdmin() === true || me.isArtisan() === true) ? undefined : "true").btn.btn-primary#save-button Save h3(data-i18n="achievement.edit_achievement_title") Edit Achievement span @@ -23,6 +23,9 @@ block content #achievement-view.clearfix + h3(data-i18n="resources.patches") Patches + .patches-view + hr else diff --git a/app/templates/editor/achievement/table.jade b/app/templates/editor/achievement/table.jade index 4559222aa..fae5782e1 100644 --- a/app/templates/editor/achievement/table.jade +++ b/app/templates/editor/achievement/table.jade @@ -8,7 +8,7 @@ block tableHeader block tableBody for data in documents - - data = data.attributes + - data = data.attributes; tr td a(href="/editor/achievement/#{data.slug || data._id}") diff --git a/app/templates/editor/article/edit.jade b/app/templates/editor/article/edit.jade index 48f610ce2..63c0d85d3 100644 --- a/app/templates/editor/article/edit.jade +++ b/app/templates/editor/article/edit.jade @@ -23,7 +23,7 @@ block content #article-view - h3 Patches + h3(data-i18n="resources.patches") Patches .patches-view hr diff --git a/app/templates/editor/campaign/campaign-analytics-modal.jade b/app/templates/editor/campaign/campaign-analytics-modal.jade new file mode 100644 index 000000000..c2050199e --- /dev/null +++ b/app/templates/editor/campaign/campaign-analytics-modal.jade @@ -0,0 +1,82 @@ +extends /templates/core/modal-base + +block modal-header-content + h3 Campaign Analytics + if campaignCompletions.startDay && campaignCompletions.endDay + .input-group.input-group-sm + input.form-control#input-startday(type='text', style='width:100px;', value=campaignCompletions.startDay) + input.form-control#input-endday(type='text', style='width:100px;', value=campaignCompletions.endDay) + button.btn.btn-default.btn-sm#reload-button(style='margin-left:10px;') Reload + label(style='font-size:10px;font-weight:normal;') + span Double-click row to open level details. + span + input#option-show-left-game(type='checkbox', checked=showLeftGame) + span Show Left Game + span + input#option-show-subscriptions(type='checkbox', checked=showSubscriptions) + span Show Subscriptions + +block modal-body-content + if campaignCompletions && campaignCompletions.levels + table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt') + thead + tr + td.level Level + td Started + td Finished + td.completion-rate Completion % + td Playtime (s) + if showLeftGame + td Left Game + td LG % + td LG/s + if showSubscriptions + td Sub Shown + td Sub Purchased + tbody + - for (var i = 0; i < campaignCompletions.levels.length; i++) + tr.level(data-level-slug=campaignCompletions.levels[i].level) + td.level-name-container= campaignCompletions.levels[i].level + span.level-name-background(style="width:#{campaignCompletions.levels[i].usersRemaining || 0}%;") + td= campaignCompletions.levels[i].started + td= campaignCompletions.levels[i].finished + if campaignCompletions.levels[i].completionRate + if campaignCompletions.top3 && campaignCompletions.top3.indexOf(campaignCompletions.levels[i].level) >= 0 + td.level-completion-container(style='background-color:lightblue;')= campaignCompletions.levels[i].completionRate.toFixed(2) + svg.level-completion-background(id="background#{campaignCompletions.levels[i].level}") + else if campaignCompletions.bottom3 && campaignCompletions.bottom3.indexOf(campaignCompletions.levels[i].level) >= 0 + td.level-completion-container(style='background-color:pink;')= campaignCompletions.levels[i].completionRate.toFixed(2) + svg.level-completion-background(id="background#{campaignCompletions.levels[i].level}") + else + td.level-completion-container= campaignCompletions.levels[i].completionRate.toFixed(2) + svg.level-completion-background(id="background#{campaignCompletions.levels[i].level}") + else + td.completion-rate + if campaignCompletions.levels[i].averagePlaytime + td.level-playtime-container= campaignCompletions.levels[i].averagePlaytime.toFixed(2) + span.level-playtime-background(style="width:#{campaignCompletions.levels[i].playtimePercentage || 0}%;") + else + td + if showLeftGame + td= campaignCompletions.levels[i].dropped + if campaignCompletions.levels[i].dropPercentage + if campaignCompletions.top3DropPercentage && campaignCompletions.top3DropPercentage.indexOf(campaignCompletions.levels[i].level) >= 0 + td(style='background-color:pink;')= campaignCompletions.levels[i].dropPercentage.toFixed(2) + else + td= campaignCompletions.levels[i].dropPercentage.toFixed(2) + else + td + if campaignCompletions.levels[i].droppedPerSecond + if campaignCompletions.top3DropPerSecond && campaignCompletions.top3DropPerSecond.indexOf(campaignCompletions.levels[i].level) >= 0 + td(style='background-color:pink;')= campaignCompletions.levels[i].droppedPerSecond.toFixed(2) + else + td= campaignCompletions.levels[i].droppedPerSecond.toFixed(2) + else + td + if showSubscriptions + td= campaignCompletions.levels[i].subsShown + td= campaignCompletions.levels[i].subsPurchased + else + div Loading... + +block modal-footer \ No newline at end of file diff --git a/app/templates/editor/campaign/campaign-editor-view.jade b/app/templates/editor/campaign/campaign-editor-view.jade new file mode 100644 index 000000000..54176c7a7 --- /dev/null +++ b/app/templates/editor/campaign/campaign-editor-view.jade @@ -0,0 +1,54 @@ +extends /templates/base + +block header + if campaign.loading + nav.navbar.navbar-default(role='navigation')#campaign-editor-top-nav + .container-fluid + ul.nav.navbar-nav + li + a(href="/") + span.glyphicon-home.glyphicon + else + nav.navbar.navbar-default(role='navigation')#campaign-editor-top-nav + ul.nav.navbar-nav + li + a(href="/") + span.glyphicon-home.glyphicon + + ul.nav.navbar-nav.navbar-right + if me.isAdmin() + li#analytics-button + a + span.glyphicon-stats.glyphicon + if me.isAdmin() + li#save-button + a + span.glyphicon-floppy-disk.glyphicon + li.dropdown + a(data-toggle='dropdown') + span.glyphicon-chevron-down.glyphicon + ul.dropdown-menu + li.dropdown-header(data-i18n="common.actions") Actions + li(class=anonymous ? "disabled": "") + a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert + li(class=anonymous ? "disabled": "") + a(data-i18n="editor.pop_i18n")#pop-level-i18n-button Populate i18n + if me.isAdmin() + li#patches-button + a + span.spr.glyphicon-wrench.glyphicon + span(data-i18n="resources.patches") Patches + li.divider + li.dropdown-header(data-i18n="common.info") Info + +block outer_content + .outer-content + #left-column + #campaign-treema + + #right-column + #campaign-view + #campaign-level-view.hidden + .patches-view.hidden + +block footer diff --git a/app/templates/editor/campaign/campaign-level-view.jade b/app/templates/editor/campaign/campaign-level-view.jade new file mode 100644 index 000000000..a638db24e --- /dev/null +++ b/app/templates/editor/campaign/campaign-level-view.jade @@ -0,0 +1,130 @@ +.jumbotron + .button.close(type="button", aria-hidden="true") × + h1 + span.spr= level.get('name') + a(href="/editor/level/#{level.get('slug')}", target="_blank") (edit) + p= level.get('description') + + if analytics.startDay && analytics.endDay + .input-group.input-group-sm + input.form-control#input-startday(type='text', style='width:100px;', value=analytics.startDay) + input.form-control#input-endday(type='text', style='width:100px;', value=analytics.endDay) + button.btn.btn-default.btn-sm#reload-button(style='margin-left:10px;') Reload + + each graph in analytics.graphs + each line in graph.lines + label.line-graph-label + input.line-graph-checkbox(data-lineid="#{line.lineID}", type='checkbox', checked=line.enabled) + span #{line.description} + span + .line-graph-container + each line in graph.lines + each point in line.points + .graph-point-info-container(data-pointid="#{point.pointID}") + div(style='font-weight:bold;') #{point.day} + each value in point.values + div #{value} + + h4 Common Problems + if analytics.commonProblems.loading + div Loading... + else + table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt') + thead + tr + td Language + td Error Message + td Error Hint + td Count + tbody + - for (var i = 0; i < analytics.commonProblems.data.length && i < 20; i++) + tr + td= analytics.commonProblems.data[i].language + td= analytics.commonProblems.data[i].message + td= analytics.commonProblems.data[i].hint + td= analytics.commonProblems.data[i].count + + h4 Recent Sessions + if analytics.recentSessions.loading + div Loading... + else + div(style='font-size:10pt') Latest #{analytics.recentSessions.data.length} sessions for this level + div(style='font-size:10pt') Double-click row to open player and session + table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt') + thead + tr + td Session ID + td Player + td Code Language + td Playtime + td Complete + td Changed + td Replay + tbody + - for (var i = 0; i < analytics.recentSessions.data.length; i++) + tr.recent-session(data-player-id=analytics.recentSessions.data[i].creator, data-session-id=analytics.recentSessions.data[i]._id) + td= analytics.recentSessions.data[i]._id + td= analytics.recentSessions.data[i].creatorName || analytics.recentSessions.data[i].creator + td= analytics.recentSessions.data[i].codeLanguage + td= analytics.recentSessions.data[i].playtime + if analytics.recentSessions.data[i].state && analytics.recentSessions.data[i].state.complete + td= analytics.recentSessions.data[i].state.complete + else + td false + td= analytics.recentSessions.data[i].changed + td + button.btn.replay-button.btn-xs + .glyphicon.glyphicon-eye-open + + h4 Completion Rates + if analytics.levelCompletions.loading + div Loading... + else + table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt') + thead + tr + td Date + td Started + td Finished + td Completion % + if analytics.levelHelps.data.length === analytics.levelCompletions.data.length + td Helps Clicked + td Helps / Started + td Help Videos + td Videos / Started + tbody + - for (var i = 0; i < analytics.levelCompletions.data.length; i++) + tr + td= analytics.levelCompletions.data[i].created + td= analytics.levelCompletions.data[i].started + td= analytics.levelCompletions.data[i].finished + td= analytics.levelCompletions.data[i].rate + if analytics.levelHelps.data.length === analytics.levelCompletions.data.length && analytics.levelCompletions.data[i].created == analytics.levelHelps.data[i].day + td= analytics.levelHelps.data[i].alertHelps + analytics.levelHelps.data[i].paletteHelps + td= ((analytics.levelHelps.data[i].alertHelps + analytics.levelHelps.data[i].paletteHelps) / analytics.levelCompletions.data[i].started).toFixed(2) + td= analytics.levelHelps.data[i].videoStarts + td= (analytics.levelHelps.data[i].videoStarts / analytics.levelCompletions.data[i].started).toFixed(2) + + h4 Average Playtimes + if analytics.levelPlaytimes.loading + div Loading... + else + table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt') + thead + tr + td Date + td Average (s) + tbody + - for (var i = 0; i < analytics.levelPlaytimes.data.length; i++) + tr + td= analytics.levelPlaytimes.data[i].created + td= analytics.levelPlaytimes.data[i].average.toFixed(2) + +if level.get('tasks') + .tasks + h3 Tasks (read only) + ul.list-unstyled + for task in level.get('tasks') + li + input(type='checkbox', checked=task.complete) + span.spl= task.name diff --git a/app/templates/editor/campaign/save-campaign-modal.jade b/app/templates/editor/campaign/save-campaign-modal.jade new file mode 100644 index 000000000..7cee2c363 --- /dev/null +++ b/app/templates/editor/campaign/save-campaign-modal.jade @@ -0,0 +1,21 @@ +extends /templates/core/modal-base + +block modal-header-content + h3 Save Changes to Campaign + +block modal-body-content + if !modelsToSave.models.length + .alert.alert-info(data-i18n="delta.no_changes") No changes + + for model in modelsToSave.models + .panel.panel-default + .panel-heading + span.panel-title.spr= model.get('name') + span.text-muted= model.constructor.className + .panel-body + .delta-view(data-model-id=model.id) + +block modal-footer + .modal-footer + button(data-dismiss="modal", data-i18n="common.cancel").btn Cancel + button.btn.btn-primary(data-i18n="common.save")#save-button Save diff --git a/app/templates/editor/component/add-thang-components-modal.jade b/app/templates/editor/component/add-thang-components-modal.jade index ff6ef030c..0cc20beea 100644 --- a/app/templates/editor/component/add-thang-components-modal.jade +++ b/app/templates/editor/component/add-thang-components-modal.jade @@ -1,7 +1,7 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content - h1 Add Components + h1(data-i18n="editor.add_components") Add Components block modal-body-content form @@ -9,9 +9,9 @@ block modal-body-content for system in systems li.list-group-item div.item-title(data-toggle="collapse", data-target="##{system}").collapsed - span.glyphicon.glyphicon-chevron-down.text-muted - span.glyphicon.glyphicon-chevron-up.text-muted - a.spl= system + span.glyphicon.glyphicon-chevron-down.text-muted.spr + span.glyphicon.glyphicon-chevron-up.text-muted.spr + a= system span.spl.text-muted= nameLists[system] .collapse-panel.collapse(id=system) for component in components[system] diff --git a/app/templates/editor/component/thang-components-edit-view.jade b/app/templates/editor/component/thang-components-edit-view.jade index cbfd948c3..244a5c746 100644 --- a/app/templates/editor/component/thang-components-edit-view.jade +++ b/app/templates/editor/component/thang-components-edit-view.jade @@ -1,9 +1,8 @@ #thang-components-column.column - h3 Components - button.btn#add-components-button Add Components + h3(data-i18n="editor.level_tab_components") Components + button.btn#add-components-button(data-i18n="editor.add_components") Add Components .treema - + .column#thang-components-config-column - h3 Component Configurations + h3(data-i18n="editor.component_configs") Component Configurations #thang-component-configs - diff --git a/app/templates/editor/delta.jade b/app/templates/editor/delta.jade index 60b95cc79..8f31176aa 100644 --- a/app/templates/editor/delta.jade +++ b/app/templates/editor/delta.jade @@ -11,7 +11,7 @@ mixin deltaPanel(delta, conflict) if delta.action === 'deleted' strong(data-i18n="delta.deleted") Deleted if delta.action === 'moved-index' - strong(data-i18n="delta.modified_array") Moved Index + strong(data-i18n="delta.moved_index") Moved Index if delta.action === 'text-diff' strong(data-i18n="delta.text_diff") Text Diff span @@ -45,4 +45,4 @@ mixin deltaPanel(delta, conflict) +deltaPanel(delta) if !deltas.length alert.alert-warning(data-i18n="delta.no_changes") No changes - \ No newline at end of file + diff --git a/app/templates/docs/components-documentation-view.jade b/app/templates/editor/docs/components-documentation-view.jade similarity index 100% rename from app/templates/docs/components-documentation-view.jade rename to app/templates/editor/docs/components-documentation-view.jade diff --git a/app/templates/docs/systems-documentation-view.jade b/app/templates/editor/docs/systems-documentation-view.jade similarity index 100% rename from app/templates/docs/systems-documentation-view.jade rename to app/templates/editor/docs/systems-documentation-view.jade diff --git a/app/templates/editor/fork-modal.jade b/app/templates/editor/fork-modal.jade index 2b4db3463..8da18b75c 100644 --- a/app/templates/editor/fork-modal.jade +++ b/app/templates/editor/fork-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="editor.fork_title") Fork New Version diff --git a/app/templates/editor/level/add-thangs-view.jade b/app/templates/editor/level/add-thangs-view.jade index 561494262..370e72013 100644 --- a/app/templates/editor/level/add-thangs-view.jade +++ b/app/templates/editor/level/add-thangs-view.jade @@ -1,5 +1,5 @@ h3(data-i18n="editor.level_tab_thangs_add") Add Thangs -input(type="search", id="thang-search", placeholder="Search thangs") +input(type="search", id="thang-search", data-i18n="[placeholder]editor.level_tab_thangs_search", placeholder="Search thangs") div.editor-nano-container.nano #thangs-list.nano-content for group in groups @@ -7,4 +7,4 @@ div.editor-nano-container.nano for thangType in group.thangs div.add-thang-palette-icon(data-thang-type=thangType.get('name'), title=thangType.get('name')) img(src=thangType.getPortraitURL(), alt="") - div.clearfix \ No newline at end of file + div.clearfix diff --git a/app/templates/editor/level/component/level-component-edit-view.jade b/app/templates/editor/level/component/level-component-edit-view.jade index 422ba16b7..a98cc6db0 100644 --- a/app/templates/editor/level/component/level-component-edit-view.jade +++ b/app/templates/editor/level/component/level-component-edit-view.jade @@ -22,23 +22,23 @@ nav.navbar.navbar-default(role='navigation') span.glyphicon-chevron-down.glyphicon ul.dropdown-menu - li.dropdown-header Actions + li.dropdown-header(data-i18n="common.actions") Actions li a#component-watch-button span.watch span.glyphicon.glyphicon-eye-open - span.spl Watch + span.spl(data-i18n="common.watch") Watch span.unwatch.secret span.glyphicon.glyphicon-eye-close - span.spl Unwatch + span.spl(data-i18n="common.unwatch") Unwatch if !me.get('anonymous') li#create-new-component-button - a(data-i18n="editor.level_component_b_new") Create New Component + a(data-i18n="editor.level_component_btn_new") Create New Component li a(data-i18n="editor.pop_i18n")#pop-component-i18n-button Populate i18n li.divider - li.dropdown-header Info + li.dropdown-header(data-i18n="common.info") Info li#component-history-button a(data-i18n="general.version_history") Version History @@ -51,4 +51,4 @@ nav.navbar.navbar-default(role='navigation') .tab-pane#component-settings #edit-component-treema .tab-pane#component-patches - .patches-view \ No newline at end of file + .patches-view diff --git a/app/templates/editor/level/component/new.jade b/app/templates/editor/level/component/new.jade index 139ecb616..94aea8a77 100644 --- a/app/templates/editor/level/component/new.jade +++ b/app/templates/editor/level/component/new.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="editor.new_component_title") Create New Component diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index 8a2a65458..f12e3df08 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -44,10 +44,10 @@ block header span.navbar-brand #{level.attributes.name} ul.nav.navbar-nav.navbar-right - li#undo-button(title="Undo (Ctrl+Z)") + li#undo-button a span.glyphicon-arrow-left.glyphicon - li#redo-button(title="Redo (Ctrl+Shift+Z)") + li#redo-button a span.glyphicon-arrow-right.glyphicon if authorized @@ -72,14 +72,14 @@ block header a.play-with-team-button(data-team=match.yourTeam, data-opponent=match.opponentSessionID)= match.yourTeam + ' vs. ' + match.opponentName else - li(title="⌃↩ or ⌘↩: Play preview of current level")#play-button + li(data-i18n="[title]general.play_preview", title="Play preview of current level")#play-button a span.glyphicon-play.glyphicon li.dropdown a(data-toggle='dropdown') span.glyphicon-chevron-down.glyphicon ul.dropdown-menu - li.dropdown-header Actions + li.dropdown-header(data-i18n="common.actions") Actions li a#level-watch-button span.watch @@ -92,17 +92,17 @@ block header li(class=anonymous ? "disabled": "") a(data-i18n="common.fork")#fork-start-button Fork li(class=anonymous ? "disabled": "") - a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert + a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert", disabled=anonymous)#revert-button Revert li(class=anonymous ? "disabled": "") - a(data-toggle="coco-modal", data-target="editor/level/modals/GenerateTerrainModal", data-i18n="editor.generate_terrain").generate-terrain-button Generate Terrain + a(data-toggle="coco-modal", data-target="editor/level/modals/GenerateTerrainModal", data-i18n="editor.generate_terrain", disabled=anonymous).generate-terrain-button Generate Terrain li(class=anonymous ? "disabled": "") a(data-i18n="editor.pop_i18n")#pop-level-i18n-button Populate i18n li.divider - li.dropdown-header Info + li.dropdown-header(data-i18n="common.info") Info li#level-history-button a(href='#', data-i18n="general.version_history") Version History li.divider - li.dropdown-header Help + li.dropdown-header(data-i18n="common.help") Help li a(href='https://github.com/codecombat/codecombat/wiki/Artisan-Home', data-i18n="editor.wiki", target="_blank") Wiki li diff --git a/app/templates/editor/level/modal/generate-terrain-modal.jade b/app/templates/editor/level/modal/generate-terrain-modal.jade index e97cd739f..f522ba5b5 100644 --- a/app/templates/editor/level/modal/generate-terrain-modal.jade +++ b/app/templates/editor/level/modal/generate-terrain-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="editor.pick_a_terrain") Pick a Terrain @@ -6,11 +6,11 @@ block modal-header-content block modal-body-content div#normal-view each sizeObject, size in presetSizes - each preset in presets + each preset in presets a(href="#") div.choose-option(data-preset-type=preset.type, data-preset-size=size, style="background:url(/images/pages/editor/level/preset_"+preset.type+"_"+size+".jpg) no-repeat center; background-size: cover") div.preset-size.name-label.capitalize - span(data-i18n="editor."+size) #{size} + span(data-i18n="editor."+size) #{size} div.preset-name.capitalize span(data-i18n="editor."+preset.type) #{preset.type} .clearfix diff --git a/app/templates/editor/level/modal/new-achievement.jade b/app/templates/editor/level/modal/new-achievement.jade index fef533444..3c083e204 100644 --- a/app/templates/editor/level/modal/new-achievement.jade +++ b/app/templates/editor/level/modal/new-achievement.jade @@ -1,4 +1,4 @@ -extends /templates/modal/new_model +extends /templates/editor/modal/new-model-modal block modal-body-content form.form diff --git a/app/templates/editor/level/modal/world-select-modal.jade b/app/templates/editor/level/modal/world-select-modal.jade index 0c48822c2..455c7c8bc 100644 --- a/app/templates/editor/level/modal/world-select-modal.jade +++ b/app/templates/editor/level/modal/world-select-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content diff --git a/app/templates/editor/level/related-achievements.jade b/app/templates/editor/level/related-achievements.jade index 33ff9230f..934ea706d 100644 --- a/app/templates/editor/level/related-achievements.jade +++ b/app/templates/editor/level/related-achievements.jade @@ -1,4 +1,4 @@ -button.btn.btn-primary#new-achievement-button(disabled=me.isAdmin() === true ? undefined : "true" data-i18n="editor.new_achievement_title") Create a New Achievement +button.btn.btn-primary#new-achievement-button(disabled=(me.isAdmin() === true || me.isArtisan() === true) ? undefined : "true" data-i18n="editor.new_achievement_title") Create a New Achievement if !achievements.models.length .panel diff --git a/app/templates/editor/level/save.jade b/app/templates/editor/level/save.jade index ae119168a..1d7a6cd70 100644 --- a/app/templates/editor/level/save.jade +++ b/app/templates/editor/level/save.jade @@ -1,7 +1,14 @@ -extends /templates/modal/save_version +extends /templates/editor/modal/save-version-modal block modal-body-content - h3= "Level: " + level.get('name') + " - " + (levelNeedsSave ? "Modified" : "Not Modified") + h3 + span(data-i18n="resources.level") Level + span=": " + level.get('name') + " - " + if levelNeedsSave + span(data-i18n="delta.modified") Modified + else + span(data-i18n="delta.not_modified") Not Modified + if levelNeedsSave .changes-stub form#save-level-form.form-inline @@ -23,7 +30,9 @@ block modal-body-content each component in modifiedComponents - var id = component.get('_id') - h4= "Component: " + component.get('system') + '.' + component.get('name') + h4 + span(data-i18n="resources.component") Component + span= ": " + component.get('system') + '.' + component.get('name') .changes-stub form.form-inline.component-form(id="save-component-" + id + "-form") input(name="component-original", type="hidden", value=component.get('original')) @@ -41,7 +50,9 @@ block modal-body-content each system in modifiedSystems - var id = system.get('_id') - h4= "System: " + system.get('name') + h4 + span(data-i18n="resources.system") System + span= ": " + system.get('name') .changes-stub form.form-inline.system-form(id="save-system-" + id + "-form") input(name="system-original", type="hidden", value=system.get('original')) diff --git a/app/templates/editor/level/system/add.jade b/app/templates/editor/level/system/add.jade index 8ed1ecbc6..443bd23ad 100644 --- a/app/templates/editor/level/system/add.jade +++ b/app/templates/editor/level/system/add.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="editor.add_system_title") Add Systems to Level @@ -6,5 +6,5 @@ block modal-header-content block modal-body-content ul.available-systems-list -block modal-footer-content - button.btn.btn-primary(data-dismiss="modal") Done Adding +block modal-footer-content + button.btn.btn-primary(data-dismiss="modal", data-i18n="editor.done_adding") Done Adding diff --git a/app/templates/editor/level/system/level-system-edit-view.jade b/app/templates/editor/level/system/level-system-edit-view.jade index f5c64f6ab..c76fc5bc9 100644 --- a/app/templates/editor/level/system/level-system-edit-view.jade +++ b/app/templates/editor/level/system/level-system-edit-view.jade @@ -2,16 +2,16 @@ nav.navbar.navbar-default(role='navigation') ul.nav.navbar-nav.nav-tabs li.active - a(href="#system-code" data-toggle="tab")#system-code-tab Code + a(href="#system-code" data-toggle="tab" data-i18n="general.code")#system-code-tab Code li - a(href="#system-config-schema" data-toggle="tab")#system-config-schema-tab Config Schema + a(href="#system-config-schema" data-toggle="tab" data-i18n="editor.level_component_config_schema")#system-config-schema-tab Config Schema li - a(href="#system-settings" data-toggle="tab")#system-settings-tab Settings + a(href="#system-settings" data-toggle="tab" data-i18n="editor.level_component_settings")#system-settings-tab Settings li a(href="#system-patches" data-toggle="tab" data-i18n="resources.patches")#system-patches-tab Patches ul.nav.navbar-nav.navbar-right - if !me.isAdmin() + if !me.isAdmin() && !me.isArtisan() li#patch-system-button a(data-i18n="[title]common.submit_patch") span.glyphicon-floppy-disk.glyphicon @@ -19,20 +19,20 @@ nav.navbar.navbar-default(role='navigation') a(data-toggle='dropdown') span.glyphicon-chevron-down.glyphicon ul.dropdown-menu - li.dropdown-header Actions + li.dropdown-header(data-i18n="common.actions") Actions li a#system-watch-button span.watch span.glyphicon.glyphicon-eye-open - span.spl Watch + span.spl(data-i18n="common.watch") Watch span.unwatch.secret span.glyphicon.glyphicon-eye-close - span.spl Unwatch + span.spl(data-i18n="common.unwatch") Unwatch if me.isAdmin() li#create-new-system a(data-i18n="editor.level_system_btn_new") Create New System li.divider - li.dropdown-header Info + li.dropdown-header(data-i18n="common.info") Info li#system-history-button a(data-i18n="general.version_history") Version History @@ -47,4 +47,4 @@ nav.navbar.navbar-default(role='navigation') .tab-pane#system-settings #edit-system-treema .tab-pane#system-patches - .patches-view \ No newline at end of file + .patches-view diff --git a/app/templates/editor/level/system/new.jade b/app/templates/editor/level/system/new.jade index 7bdf9f762..84734462a 100644 --- a/app/templates/editor/level/system/new.jade +++ b/app/templates/editor/level/system/new.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="editor.create_system_title") Create New System diff --git a/app/templates/editor/level/thangs-tab-view.jade b/app/templates/editor/level/thangs-tab-view.jade index b9fbe46f9..9c5e6fe09 100644 --- a/app/templates/editor/level/thangs-tab-view.jade +++ b/app/templates/editor/level/thangs-tab-view.jade @@ -4,11 +4,11 @@ button.btn#thangs-palette-toggle span.icon-plus .thangs-container.hide#all-thangs h3(data-i18n="editor.level_tab_thangs_title") Current Thangs - #thangs-treema(title="Double click to configure a thang") + #thangs-treema(data-i18n="[title]editor.config_thang", title="Double click to configure a thang") .world-container #canvas-wrapper - button.generate-terrain-button.btn.btn-info.btn-lg(data-toggle="coco-modal", data-target="editor/level/modals/GenerateTerrainModal", data-i18n="editor.generate_terrain", title="Generate Terrain") Generate Terrain + button.generate-terrain-button.btn.btn-info.btn-lg(data-toggle="coco-modal", data-target="editor/level/modals/GenerateTerrainModal", data-i18n="editor.generate_terrain") Generate Terrain ul.dropdown-menu#contextmenu li#delete a(data-i18n="editor.delete") Delete @@ -31,4 +31,4 @@ button.btn#thangs-palette-toggle #canvas-top-gradient.gradient #add-thangs-view -#level-thang-edit-view.secret \ No newline at end of file +#level-thang-edit-view.secret diff --git a/app/templates/modal/confirm.jade b/app/templates/editor/modal/confirm-modal.jade similarity index 90% rename from app/templates/modal/confirm.jade rename to app/templates/editor/modal/confirm-modal.jade index f7d746de4..bd8cc708c 100644 --- a/app/templates/modal/confirm.jade +++ b/app/templates/editor/modal/confirm-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3 #{confirmTitle} diff --git a/app/templates/modal/new_model.jade b/app/templates/editor/modal/new-model-modal.jade similarity index 85% rename from app/templates/modal/new_model.jade rename to app/templates/editor/modal/new-model-modal.jade index c904d1b96..1e6e78f4f 100644 --- a/app/templates/modal/new_model.jade +++ b/app/templates/editor/modal/new-model-modal.jade @@ -1,7 +1,7 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content - h3(data-i18n="#{currentNew}") Create New #{modelLabel} + h3(data-i18n="#{newModelTitle}") Create New #{modelLabel} block modal-body-content form.form diff --git a/app/templates/modal/save_version.jade b/app/templates/editor/modal/save-version-modal.jade similarity index 71% rename from app/templates/modal/save_version.jade rename to app/templates/editor/modal/save-version-modal.jade index 748c541a9..03201a653 100644 --- a/app/templates/modal/save_version.jade +++ b/app/templates/editor/modal/save-version-modal.jade @@ -1,8 +1,8 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content if isPatch - h3(data-i18n="versions.submit_patch_title") Submit Patch + h3(data-i18n="common.submit_patch") Submit Patch else h3(data-i18n="versions.save_version_title") Save New Version @@ -18,7 +18,7 @@ block modal-body-content input#major-version(name="version-is-major", type="checkbox") span(data-i18n="versions.new_major_version") New Major Version else - .alert.alert-danger No changes + .alert.alert-danger(data-i18n="delta.no_changes") No changes block modal-body-wait-content if hasChanges @@ -30,17 +30,16 @@ block modal-body-wait-content block modal-footer-content if hasChanges #accept-cla-wrapper.alert.alert-info - span(data-i18n="versions.cla_prefix") To save changes, first you must agree to our - | + span.spr(data-i18n="versions.cla_prefix") To save changes, first you must agree to our strong#cla-link(data-i18n="versions.cla_url") CLA span(data-i18n="versions.cla_suffix") . button.btn.btn-sm#agreement-button(data-i18n="versions.cla_agree") I AGREE if isPatch - .alert.alert-info An owner will need to approve it before your changes will become visible. - + .alert.alert-info(data-i18n="versions.owner_approve") An owner will need to approve it before your changes will become visible. + .buttons button.btn(data-dismiss="modal", data-i18n="common.cancel") Cancel if hasChanges && !isPatch button.btn.btn-primary#save-version-button(data-i18n="common.save") Save if hasChanges && isPatch - button.btn.btn-primary#submit-patch-button(data-i18n="versions.submit_patch") Submit Patch \ No newline at end of file + button.btn.btn-primary#submit-patch-button(data-i18n="common.submit_patch") Submit Patch diff --git a/app/templates/modal/versions.jade b/app/templates/editor/modal/versions-modal.jade similarity index 87% rename from app/templates/modal/versions.jade rename to app/templates/editor/modal/versions-modal.jade index 827230ad2..bf38149e6 100755 --- a/app/templates/modal/versions.jade +++ b/app/templates/editor/modal/versions-modal.jade @@ -1,11 +1,11 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content if dataList h3 span(data-i18n="general.version_history_for") Version History for: |"#{dataList[0].name}" - p + p(data-i18n="general.select_changes") |Select two changes below to see the difference. div.delta-container @@ -30,4 +30,4 @@ block modal-body-content td= data.creator td #{data.commitMessage} -block modal-footer-content \ No newline at end of file +block modal-footer-content diff --git a/app/templates/editor/patch_modal.jade b/app/templates/editor/patch_modal.jade index 50549331e..a1534b071 100644 --- a/app/templates/editor/patch_modal.jade +++ b/app/templates/editor/patch_modal.jade @@ -1,8 +1,8 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content .modal-header-content - h3 Patch + h3(data-i18n="resources.patch") Patch block modal-body-content .modal-body @@ -24,4 +24,4 @@ block modal-footer button.btn.btn-primary#accept-button Accept if status != 'rejected' button.btn.btn-danger#reject-button Reject - \ No newline at end of file + diff --git a/app/templates/editor/patches.jade b/app/templates/editor/patches.jade index 872788e7d..487067d54 100644 --- a/app/templates/editor/patches.jade +++ b/app/templates/editor/patches.jade @@ -1,30 +1,27 @@ .btn-group(data-toggle="buttons").status-buttons label.btn.btn-default.pending input(type="radio", name="status", value="pending") - | Pending + span(data-i18n="general.pending") Pending label.btn.btn-default.accepted input(type="radio", name="status", value="accepted") - | Accepted + span(data-i18n="general.accepted") Accepted label.btn.btn-default.rejected input(type="radio", name="status", value="rejected") - | Rejected + span(data-i18n="general.rejected") Rejected label.btn.btn-default.withdrawn input(type="radio", name="status", value="withdrawn") - | Withdrawn + span(data-i18n="general.withdrawn") Withdrawn if patches.loading - p Loading + p(data-i18n="common.loading") Loading... else table.table.table-condensed.table-bordered tr - th Submitter - th Submitted - th Commit Message - th Review + th(data-i18n="general.submitter") Submitter + th(data-i18n="general.submitted") Submitted + th(data-i18n="general.commit_msg") Commit Message for patch in patches - tr + tr.patch-row(data-patch-id=patch.id) td= patch.userName td= moment(patch.get('created')).format('llll') td= patch.get('commitMessage') - td - span.glyphicon.glyphicon-wrench(data-patch-id=patch.id).patch-icon diff --git a/app/templates/editor/poll/poll-edit-view.jade b/app/templates/editor/poll/poll-edit-view.jade new file mode 100644 index 000000000..cbd65501b --- /dev/null +++ b/app/templates/editor/poll/poll-edit-view.jade @@ -0,0 +1,31 @@ +extends /templates/base + +block content + if !unauthorized + ol.breadcrumb + li + a(href="/editor", data-i18n="editor.main_title") CodeCombat Editors + li + a(href="/editor/poll", data-i18n="editor.poll_title") Poll Editor + li.active + | #{poll.attributes.name} + + button.poll-tool-button(data-i18n="common.delete", disabled=!me.isAdmin()).btn.btn-primary#delete-button Delete + button.poll-tool-button(data-i18n="common.save", disabled=!me.isAdmin()).btn.btn-primary#save-button Save + + h3(data-i18n="poll.edit_poll_title") Edit Poll + span + |: "#{poll.attributes.name}" + + #poll-treema + + #poll-view.clearfix + + h3(data-i18n="resources.patches") Patches + .patches-view + + hr + + else + .alert.alert-danger + span Admin only. Turn around. diff --git a/app/templates/editor/poll/poll-search-table.jade b/app/templates/editor/poll/poll-search-table.jade new file mode 100644 index 000000000..7212551ea --- /dev/null +++ b/app/templates/editor/poll/poll-search-table.jade @@ -0,0 +1,19 @@ +extends /templates/common/table + +block tableHeader + tr + th(data-i18n="general.name") Name + th(data-i18n="general.description") Description + th(data-i18n="polls.priority") Priority + th(data-i18n="general.date") Date + +block tableBody + for data in documents + - data = data.attributes; + tr + td + a(href="/editor/poll/#{data.slug || data._id}") + | #{data.name} + td= data.description + td= data.priority + td= moment(data.created).fromNow() diff --git a/app/templates/editor/thang/table.jade b/app/templates/editor/thang/table.jade index 3d4552cb8..dc2445869 100755 --- a/app/templates/editor/thang/table.jade +++ b/app/templates/editor/thang/table.jade @@ -14,6 +14,7 @@ block tableHeader th(data-i18n="general.name") Name th(data-i18n="general.description") Description th(data-i18n="general.version") Version + th(data-i18n="editor.tasks") Tasks block tableBody for thang in documents @@ -25,4 +26,10 @@ block tableBody | #{thang.get('name')} td.body-row #{thang.get('description')} - var version = thang.get('version') - td #{version.major}.#{version.minor} \ No newline at end of file + td #{version.major}.#{version.minor} + - var tasks = thang.get('tasks'); + if tasks && tasks.length + - var completed = tasks.filter(function(t) { return t.complete; }); + td #{completed.length}/#{tasks.length} + else + td diff --git a/app/templates/editor/thang/thang-type-edit-view.jade b/app/templates/editor/thang/thang-type-edit-view.jade index 013410d3d..8da04e152 100644 --- a/app/templates/editor/thang/thang-type-edit-view.jade +++ b/app/templates/editor/thang/thang-type-edit-view.jade @@ -15,13 +15,13 @@ block header span.glyphicon-home.glyphicon ul.nav.navbar-nav.nav-tabs li.active - a(href="#editor-thang-main-tab-view", data-toggle="tab") Main + a(href="#editor-thang-main-tab-view", data-toggle="tab", data-i18n="editor.thang_main") Main li - a(href="#editor-thang-components-tab-view", data-toggle="tab") Components + a(href="#editor-thang-components-tab-view", data-toggle="tab", data-i18n="editor.level_tab_components") Components li - a(href="#editor-thang-spritesheets-view", data-toggle="tab") Spritesheets + a(href="#editor-thang-spritesheets-view", data-toggle="tab", data-i18n="editor.thang_spritesheets") Spritesheets li - a(href="#editor-thang-colors-tab-view", data-toggle="tab")#color-tab Colors + a(href="#editor-thang-colors-tab-view", data-toggle="tab", data-i18n="editor.thang_colors")#color-tab Colors li a(href="#editor-thang-patches-view", data-toggle="tab")#patches-tab span(data-i18n="resources.patches").spr Patches @@ -51,19 +51,19 @@ block header a(data-toggle='dropdown') span.glyphicon-chevron-down.glyphicon ul.dropdown-menu - li.dropdown-header Actions - li(class=anonymous ? "disabled": "") + li.dropdown-header(data-i18n="common.actions") Actions + li(class=!me.isAdmin() && !me.isArtisan() ? "disabled": "") a(data-i18n="common.fork")#fork-start-button Fork - li(class=anonymous ? "disabled": "") - a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert - li(class=anonymous ? "disabled": "") + li(class=!authorized ? "disabled": "") + a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert", disabled=!authorized)#revert-button Revert + li(class=!authorized ? "disabled": "") a(data-i18n="editor.pop_i18n")#pop-level-i18n-button Populate i18n li.divider - li.dropdown-header Info + li.dropdown-header(data-i18n="common.info") Info li#history-button a(href='#', data-i18n="general.version_history") Version History li.divider - li.dropdown-header Help + li.dropdown-header(data-i18n="common.help") Help li a(href='https://github.com/codecombat/codecombat/wiki/Artisan-Home', data-i18n="editor.wiki", target="_blank") Wiki li diff --git a/app/templates/editor/thang/vector-icon-setup-modal.jade b/app/templates/editor/thang/vector-icon-setup-modal.jade index cdfcb8268..3a06831ca 100644 --- a/app/templates/editor/thang/vector-icon-setup-modal.jade +++ b/app/templates/editor/thang/vector-icon-setup-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3 Choose Container for Vector Icon diff --git a/app/templates/employers.jade b/app/templates/employers.jade index d2e724cc3..1fb9fed2b 100644 --- a/app/templates/employers.jade +++ b/app/templates/employers.jade @@ -5,184 +5,184 @@ block content h1(data-i18n="employers.deprecation_warning_title") Sorry, CodeCombat is not recruiting right now. p(data-i18n="employers.deprecation_warning") We are focusing on beginner levels instead of finding expert developers for the time being. - .deprecated - .artisanal-claim - if me.get('anonymous') - a#login-link(data-i18n="login.log_in") Log In - br - if !isEmployer && !me.isAdmin() - #tagline - h1(data-i18n="employers.hire_developers_not_credentials") Hire developers, not credentials. - button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started - else - if !me.get('anonymous') - a#logout-link(data-i18n="login.log_out") Log Out - br - .row - - var fullProfiles = isEmployer || me.isAdmin(); - - if fullProfiles - #filter-column.col-md-3 - #filter - .panel-group#filter_panel - a#filter-link(data-toggle="collapse" data-target="#collapseOne") - .panel.panel-default - .panel-heading - h4.panel-title - span.glyphicon.glyphicon-folder-open#folder-icon - | Filter - .panel-collapse.collapse.in#collapseOne - .panel-body - p - strong(data-i18n="employers.already_screened") We've already technically screened all our candidates - span(data-i18n="employers.filter_further") , but you can also filter further: - form#filters - .filter_section#visa_filter - h4(data-i18n="employers.filter_visa") Visa - label - input(type="checkbox" name="visa" value="Authorized to work in the US") - span(data-i18n="employers.filter_visa_yes") US Authorized - | (#{candidatesInFilter("visa","Authorized to work in the US")}) - label - input(type="checkbox" name="visa" value="Need visa sponsorship") - span(data-i18n="employers.filter_visa_no") Not Authorized - | (#{candidatesInFilter("visa","Need visa sponsorship")}) - .filter_section#school_filter - h4(data-i18n="account_profile.education") Education - label - input(type="checkbox" name="schoolFilter" value="Top School") - span(data-i18n="employers.filter_education_top") Top School - | (#{candidatesInFilter("schoolFilter","Top School")}) - label - input(type="checkbox" name="schoolFilter" value="Other") - span(data-i18n="employers.filter_education_other") Other - | (#{candidatesInFilter("schoolFilter","Other")}) - .filter_section#role_filter - h4(data-i18n="employers.candidate_role") Role - label - input(type="checkbox" name="roleFilter" value="Web Developer") - span(data-i18n="employers.filter_role_web_developer") Web Developer - | (#{candidatesInFilter("roleFilter","Web Developer")}) - label - input(type="checkbox" name="roleFilter" value="Software Developer") - span(data-i18n="employers.filter_role_software_developer") Software Developer - | (#{candidatesInFilter("roleFilter","Software Developer")}) - label - input(type="checkbox" name="roleFilter" value="Mobile Developer") - span(data-i18n="employers.filter_role_mobile_developer") Mobile Developer - | (#{candidatesInFilter("roleFilter","Mobile Developer")}) - .filter_section#seniority_filter - h4(data-i18n="employers.filter_experience") Experience - label - input(type="checkbox" name="seniorityFilter" value="Senior") - span(data-i18n="employers.filter_experience_senior") Senior - | (#{candidatesInFilter("seniorityFilter","Senior")}) - label - input(type="checkbox" name="seniorityFilter" value="Junior") - span(data-i18n="employers.filter_experience_junior") Junior - | (#{candidatesInFilter("seniorityFilter","Junior")}) - label - input(type="checkbox" name="seniorityFilter" value="Recent Grad") - span(data-i18n="employers.filter_experience_recent_grad") Recent Grad - | (#{candidatesInFilter("seniorityFilter","Recent Grad")}) - label - input(type="checkbox" name="seniorityFilter" value="College Student") - span(data-i18n="employers.filter_experience_student") College Student - | (#{candidatesInFilter("seniorityFilter","College Student")}) - - //input#select_all_checkbox(type="checkbox" name="select_all" checked) - //| Select all - p#results - | #{numberOfCandidates} - span(data-i18n="employers.results") results - h4#filter-alerts-heading Filter Email Alerts - p Get an email whenever a candidate meeting certain criteria enters the system. - table#saved-filter-table - thead - tr - th Filters - th Remove - tbody - button.btn#create-alert-button Create Alert with Current Filters - - #candidates-column(class=fullProfiles ? "full-profiles col-md-9" : "teaser-profiles col-md-12") - if candidates.length - #candidate-table - table - tbody - for candidate, index in featuredCandidates - - var profile = candidate.get('jobProfile'); - - var authorized = candidate.id; // If we have the id, then we are authorized. - - var profileAge = (new Date() - new Date(profile.updated)) / 86400 / 1000; - - var expired = profileAge > 2 * 30.4; - - var curated = profile.curated; - - var photoSize = fullProfiles ? 75 : 50; - - tr.candidate-row(data-candidate-id=candidate.id, id=candidate.id, class=expired ? "expired" : "") - td(rowspan=3) - - var photoURL = candidate.getPhotoURL(photoSize, false, true); - div(class="candidate-picture " + (/^\/file/.test(photoURL) ? "" : "anonymous"), style='background-image: url(' + encodeURI(photoURL) + ')') - if fullProfiles - td.candidate-name-cell - strong= profile.name - | - - span= profile.jobTitle - tr.description_row(data-candidate-id=candidate.id) - if curated && curated.shortDescription - td.candidate-description - div #{curated.shortDescription} - else - td.candidate-description - div #{profile.shortDescription} - tr.border_row(data-candidate-id=candidate.id) - if curated - - var workHistory = curated.workHistory.join(","); - if !fullProfiles - td.tag_column - img(src="/images/pages/employer/tag.png") - | #{profile.jobTitle} - td.location_column - img(src="/images/pages/employer/location.png") - | #{curated.location} - td.education_column - img(src="/images/pages/employer/education.png") - | #{curated.education} - td.work_column - if workHistory - img(src="/images/pages/employer/briefcase.png") - | #{workHistory} - - if !fullProfiles - div#info_wrapper - span.hiring-call-to-action - h2#start-hiring(data-i18n="employers.start_hiring") Start hiring. - button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started - - h2#hiring-reasons.hiring-call-to-action(data-i18n="employers.reasons") Three reasons you should hire through us: - .reasons#top_row - .reason - img.employer_icon(src="/images/pages/employer/employer_icon2.png") - h3(data-i18n="employers.everyone_looking") Everyone here is looking for their next opportunity. - p(data-i18n="employers.everyone_looking_blurb") Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction. - .reason - img.employer_icon(src="/images/pages/employer/employer_icon6.png") - h3(data-i18n="employers.weeding") Sit back; we've done the weeding for you. - p(data-i18n="employers.weeding_blurb") Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time. - .reason - img(class="employer_icon" src="/images/pages/employer/employer_icon3.png") - h3(data-i18n="employers.pass_screen") They will pass your technical screen. - p(data-i18n="employers.pass_screen_blurb") Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News. - span.hiring-call-to-action - h2(data-i18n="employers.make_hiring_easier") Make my hiring easier, please. - button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started - .reasons#bottom_row - .reason_long - img.employer_icon(src="/images/pages/employer/employer_icon1.png") - .reason_text - h3(data-i18n="employers.what") What is CodeCombat? - p(data-i18n="employers.what_blurb") CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. We support JavaScript, Python, Lua, Clojure, CoffeeScript, and Io. - .reason_long - img.employer_icon(src="/images/pages/employer/employer_icon5.png") - .reason_text - h3(data-i18n="employers.cost") How much do we charge? - p(data-i18n="employers.cost_blurb") We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company. + //.deprecated + // .artisanal-claim + // if me.get('anonymous') + // a#login-link(data-i18n="login.log_in") Log In + // br + // if !isEmployer && !me.isAdmin() + // #tagline + // h1(data-i18n="employers.hire_developers_not_credentials") Hire developers, not credentials. + // button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started + // else + // if !me.get('anonymous') + // a#logout-link(data-i18n="login.log_out") Log Out + // br + // .row + // - var fullProfiles = isEmployer || me.isAdmin(); + // + // if fullProfiles + // #filter-column.col-md-3 + // #filter + // .panel-group#filter_panel + // a#filter-link(data-toggle="collapse" data-target="#collapseOne") + // .panel.panel-default + // .panel-heading + // h4.panel-title + // span.glyphicon.glyphicon-folder-open#folder-icon + // | Filter + // .panel-collapse.collapse.in#collapseOne + // .panel-body + // p + // strong(data-i18n="employers.already_screened") We've already technically screened all our candidates + // span(data-i18n="employers.filter_further") , but you can also filter further: + // form#filters + // .filter_section#visa_filter + // h4(data-i18n="employers.filter_visa") Visa + // label + // input(type="checkbox" name="visa" value="Authorized to work in the US") + // span(data-i18n="employers.filter_visa_yes") US Authorized + // | (#{candidatesInFilter("visa","Authorized to work in the US")}) + // label + // input(type="checkbox" name="visa" value="Need visa sponsorship") + // span(data-i18n="employers.filter_visa_no") Not Authorized + // | (#{candidatesInFilter("visa","Need visa sponsorship")}) + // .filter_section#school_filter + // h4(data-i18n="account_profile.education") Education + // label + // input(type="checkbox" name="schoolFilter" value="Top School") + // span(data-i18n="employers.filter_education_top") Top School + // | (#{candidatesInFilter("schoolFilter","Top School")}) + // label + // input(type="checkbox" name="schoolFilter" value="Other") + // span(data-i18n="employers.filter_education_other") Other + // | (#{candidatesInFilter("schoolFilter","Other")}) + // .filter_section#role_filter + // h4(data-i18n="employers.candidate_role") Role + // label + // input(type="checkbox" name="roleFilter" value="Web Developer") + // span(data-i18n="employers.filter_role_web_developer") Web Developer + // | (#{candidatesInFilter("roleFilter","Web Developer")}) + // label + // input(type="checkbox" name="roleFilter" value="Software Developer") + // span(data-i18n="employers.filter_role_software_developer") Software Developer + // | (#{candidatesInFilter("roleFilter","Software Developer")}) + // label + // input(type="checkbox" name="roleFilter" value="Mobile Developer") + // span(data-i18n="employers.filter_role_mobile_developer") Mobile Developer + // | (#{candidatesInFilter("roleFilter","Mobile Developer")}) + // .filter_section#seniority_filter + // h4(data-i18n="employers.filter_experience") Experience + // label + // input(type="checkbox" name="seniorityFilter" value="Senior") + // span(data-i18n="employers.filter_experience_senior") Senior + // | (#{candidatesInFilter("seniorityFilter","Senior")}) + // label + // input(type="checkbox" name="seniorityFilter" value="Junior") + // span(data-i18n="employers.filter_experience_junior") Junior + // | (#{candidatesInFilter("seniorityFilter","Junior")}) + // label + // input(type="checkbox" name="seniorityFilter" value="Recent Grad") + // span(data-i18n="employers.filter_experience_recent_grad") Recent Grad + // | (#{candidatesInFilter("seniorityFilter","Recent Grad")}) + // label + // input(type="checkbox" name="seniorityFilter" value="College Student") + // span(data-i18n="employers.filter_experience_student") College Student + // | (#{candidatesInFilter("seniorityFilter","College Student")}) + // + // //input#select_all_checkbox(type="checkbox" name="select_all" checked) + // //| Select all + // p#results + // | #{numberOfCandidates} + // span(data-i18n="employers.results") results + // h4#filter-alerts-heading Filter Email Alerts + // p Get an email whenever a candidate meeting certain criteria enters the system. + // table#saved-filter-table + // thead + // tr + // th Filters + // th Remove + // tbody + // button.btn#create-alert-button Create Alert with Current Filters + // + // #candidates-column(class=fullProfiles ? "full-profiles col-md-9" : "teaser-profiles col-md-12") + // if candidates.length + // #candidate-table + // table + // tbody + // for candidate, index in featuredCandidates + // - var profile = candidate.get('jobProfile'); + // - var authorized = candidate.id; // If we have the id, then we are authorized. + // - var profileAge = (new Date() - new Date(profile.updated)) / 86400 / 1000; + // - var expired = profileAge > 2 * 30.4; + // - var curated = profile.curated; + // - var photoSize = fullProfiles ? 75 : 50; + // + // tr.candidate-row(data-candidate-id=candidate.id, id=candidate.id, class=expired ? "expired" : "") + // td(rowspan=3) + // - var photoURL = candidate.getPhotoURL(photoSize, false, true); + // div(class="candidate-picture " + (/^\/file/.test(photoURL) ? "" : "anonymous"), style='background-image: url(' + encodeURI(photoURL) + ')') + // if fullProfiles + // td.candidate-name-cell + // strong= profile.name + // | - + // span= profile.jobTitle + // tr.description_row(data-candidate-id=candidate.id) + // if curated && curated.shortDescription + // td.candidate-description + // div #{curated.shortDescription} + // else + // td.candidate-description + // div #{profile.shortDescription} + // tr.border_row(data-candidate-id=candidate.id) + // if curated + // - var workHistory = curated.workHistory.join(","); + // if !fullProfiles + // td.tag_column + // img(src="/images/pages/employer/tag.png") + // | #{profile.jobTitle} + // td.location_column + // img(src="/images/pages/employer/location.png") + // | #{curated.location} + // td.education_column + // img(src="/images/pages/employer/education.png") + // | #{curated.education} + // td.work_column + // if workHistory + // img(src="/images/pages/employer/briefcase.png") + // | #{workHistory} + // + // if !fullProfiles + // div#info_wrapper + // span.hiring-call-to-action + // h2#start-hiring(data-i18n="employers.start_hiring") Start hiring. + // button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started + // + // h2#hiring-reasons.hiring-call-to-action(data-i18n="employers.reasons") Three reasons you should hire through us: + // .reasons#top_row + // .reason + // img.employer_icon(src="/images/pages/employer/employer_icon2.png") + // h3(data-i18n="employers.everyone_looking") Everyone here is looking for their next opportunity. + // p(data-i18n="employers.everyone_looking_blurb") Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction. + // .reason + // img.employer_icon(src="/images/pages/employer/employer_icon6.png") + // h3(data-i18n="employers.weeding") Sit back; we've done the weeding for you. + // p(data-i18n="employers.weeding_blurb") Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time. + // .reason + // img(class="employer_icon" src="/images/pages/employer/employer_icon3.png") + // h3(data-i18n="employers.pass_screen") They will pass your technical screen. + // p(data-i18n="employers.pass_screen_blurb") Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News. + // span.hiring-call-to-action + // h2(data-i18n="employers.make_hiring_easier") Make my hiring easier, please. + // button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started + // .reasons#bottom_row + // .reason_long + // img.employer_icon(src="/images/pages/employer/employer_icon1.png") + // .reason_text + // h3(data-i18n="employers.what") What is CodeCombat? + // p(data-i18n="employers.what_blurb") CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. We support JavaScript, Python, Lua, Clojure, CoffeeScript, and Io. + // .reason_long + // img.employer_icon(src="/images/pages/employer/employer_icon5.png") + // .reason_text + // h3(data-i18n="employers.cost") How much do we charge? + // p(data-i18n="employers.cost_blurb") We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company. diff --git a/app/templates/front-view.jade b/app/templates/front-view.jade deleted file mode 100644 index 340235513..000000000 --- a/app/templates/front-view.jade +++ /dev/null @@ -1,28 +0,0 @@ -extends /templates/base - -block content - - .page-header - h1#site-slogan - span(data-i18n="home.slogan") Learn to Code by Playing a Game - small.spl.spr - - small free! - - .row.platform-choices - .col-xs-6.platform-ios - a(href="#") - .panel.panel-default - .panel-body - h1 Play on iPad - img.img-responsive(src="/images/pages/front/play_web.jpg") - p.lead Get the app! - - .col-xs-6.platform-web - a(href="/play-hero") - .panel.panel-default - .panel-body - h1 Play on web - img.img-responsive(src="/images/pages/front/play_web.jpg") - p.lead Play right now! - - .clearfix diff --git a/app/templates/front.jade b/app/templates/front.jade deleted file mode 100644 index cdd11c19f..000000000 --- a/app/templates/front.jade +++ /dev/null @@ -1,26 +0,0 @@ -extends /templates/base - -block content - - .page-header - h1#site-slogan - span(data-i18n="home.slogan") Learn to Code by Playing a Game - small.spl.spr - - small free! - - .row.platform-choices - .col-xs-6.platform-ios - a(href="#") - .panel.panel-default - .panel-body - h1 Play on iPad - em - get the app! - - .col-xs-6.platform-web - a(href="/play") - .panel.panel-default - .panel-body - h1 Play on web - em - play right now! - - .clearfix diff --git a/app/templates/game-menu/guide-view.jade b/app/templates/game-menu/guide-view.jade deleted file mode 100644 index 81c2613e5..000000000 --- a/app/templates/game-menu/guide-view.jade +++ /dev/null @@ -1,7 +0,0 @@ -ul.nav.nav-tabs - for doc in docs - li - a(data-target="#docs_tab_#{doc.slug}", data-toggle="tab") #{doc.name} -div.tab-content - for doc in docs - div.tab-pane(id="docs_tab_#{doc.slug}")!= doc.html diff --git a/app/templates/home.jade b/app/templates/home-view.jade similarity index 66% rename from app/templates/home.jade rename to app/templates/home-view.jade index 2728ae309..8ad164d34 100644 --- a/app/templates/home.jade +++ b/app/templates/home-view.jade @@ -3,28 +3,32 @@ extends /templates/base block outer_content #spacer - a#play-button(href="/play", data-i18n="home.play") + a#play-button(href="/play", data-i18n="[html]home.play") - //#or-ipad(data-i18n="home.or_ipad") - - //img(src="/images/pages/home/app_store_badge.svg")#apple-store-button + //a(href="https://itunes.apple.com/us/app/codecombat/id936523909?mt=8") + // #or-ipad(data-i18n="home.or_ipad") + // + //a(href="https://itunes.apple.com/us/app/codecombat/id936523909?mt=8") + // img(src="/images/pages/home/app_store_badge.svg")#apple-store-button #slogan(data-i18n="home.slogan") + //- TODO: This does not work on IE8 .alert.alert-danger.lt-ie9 strong(data-i18n="home.no_ie") CodeCombat does not run in Internet Explorer 8 or older. Sorry! - if isMobile + if isIPadBrowser || isMobile .alert.alert-danger.mobile strong(data-i18n="home.no_mobile") CodeCombat wasn't designed for mobile devices and may not work! - if isOldBrowser + else if isOldBrowser .alert.alert-danger.old-browser strong(data-i18n="home.old_browser") Uh oh, your browser is too old to run CodeCombat. Sorry! br span(data-i18n="home.old_browser_suffix") You can try anyway, but it probably won't work. + block extra_footer_content - if true || explainHourOfCode + if explainHourOfCode // Does not show up unless lang is en-US. div.hour-of-code-explanation | The 'Hour of Code' is a nationwide initiative by diff --git a/app/templates/i18n/i18n-edit-model-view.jade b/app/templates/i18n/i18n-edit-model-view.jade index 596650e01..efa2f4a9b 100644 --- a/app/templates/i18n/i18n-edit-model-view.jade +++ b/app/templates/i18n/i18n-edit-model-view.jade @@ -21,7 +21,7 @@ block header ul.nav.navbar-nav.navbar-right li - button.btn.btn-info.btn-sm.pull-right#patch-submit(disabled=model.hasLocalChanges() ? null : 'disabled', value=model.id) Submit Changes + button.btn.btn-info.btn-sm.pull-right#patch-submit(disabled=model.hasLocalChanges() ? null : 'disabled', value=model.id, data-i18n="common.submit_changes") Submit Changes li.dropdown @@ -29,17 +29,17 @@ block header span.glyphicon-chevron-down.glyphicon ul.dropdown-menu - li.dropdown-header Actions + li.dropdown-header(data-i18n="common.actions") Actions li(class=anonymous ? "disabled": "") a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert li.divider - li.dropdown-header Info + li.dropdown-header(data-i18n="common.info") Info li#history-button a(href='#', data-i18n="general.version_history") Version History li.divider - li.dropdown-header Help + li.dropdown-header(data-i18n="common.help") Help li a(href='https://github.com/codecombat/codecombat/wiki', data-i18n="editor.wiki", target="_blank") Wiki li @@ -54,12 +54,14 @@ block outer_content select.form-control#language-select - table.table - for row in translationList - tr(data-format=row.format || '') + for row in translationList + table.table + tr th= row.title + tr(data-format=row.format || '') td.english-value-row div= row.enValue + tr(data-format=row.format || '') td.to-value-row if row.format === 'markdown' div(data-index=row.index.toString())= row.toValue diff --git a/app/templates/i18n/i18n-home-view.jade b/app/templates/i18n/i18n-home-view.jade index 4d1e7805d..5e14b40e3 100644 --- a/app/templates/i18n/i18n-home-view.jade +++ b/app/templates/i18n/i18n-home-view.jade @@ -11,16 +11,24 @@ block content select#language-select.form-control.input-sm option(value='') Select one... + th Translated Name th Type th Specifically Covered - th Generally Covered + if showGeneralCoverage + th Generally Covered if selectedLanguage for model in collection.models + -var translatedName = '' + -if (model.get('i18n') != undefined) + -if (model.get('i18n').hasOwnProperty(selectedLanguage)) + -translatedName = model.get('i18n')[selectedLanguage].name + tr td a(href=model.i18nURLBase+model.get('slug'))= model.get('name') + td= translatedName td= model.constructor.className td(class=model.specificallyCovered ? 'success' : 'danger')= model.specificallyCovered ? 'Yes' : 'No' - td(class=model.generallyCovered ? 'success' : 'danger')= model.generallyCovered ? 'Yes' : 'No' - + if showGeneralCoverage + td(class=model.generallyCovered ? 'success' : 'danger')= model.generallyCovered ? 'Yes' : 'No' diff --git a/app/templates/legal.jade b/app/templates/legal.jade index 388e5940f..bb088c5de 100644 --- a/app/templates/legal.jade +++ b/app/templates/legal.jade @@ -8,7 +8,7 @@ block content hr p(data-i18n="legal.opensource_intro").lead - | CodeCombat is free to play and completely open source. + | CodeCombat is completely open source. p span(data-i18n="legal.opensource_description_prefix") | Check out @@ -18,10 +18,9 @@ block content span(data-i18n="legal.opensource_description_center") | and help out if you like! CodeCombat is built on | dozens of open source projects, and we love them. See - a(href="https://github.com/codecombat/codecombat/wiki/Third-party-software-and-services", data-i18n="legal.archmage_wiki_url") + a(href="https://github.com/codecombat/codecombat/wiki/Archmage-Home", data-i18n="legal.archmage_wiki_url") | our Archmage wiki - span - span(data-i18n="legal.opensource_description_suffix") + span.spl(data-i18n="legal.opensource_description_suffix") | for a list of the software that makes this game possible. hr @@ -34,9 +33,6 @@ block content | Privacy p(data-i18n="legal.privacy_description") | We will not sell any of your personal information. - | We intend to make money through recruitment eventually, - | but rest assured we will not distribute your personal - | information to interested companies without your explicit consent. h4(data-i18n="legal.security_title") | Security p(data-i18n="legal.security_description") @@ -46,43 +42,18 @@ block content h4(data-i18n="legal.email_title") | Email p - span(data-i18n="legal.email_description_prefix") + span.spr(data-i18n="legal.email_description_prefix") | We will not inundate you with spam. Through - span a(href='/account/settings', data-i18n="legal.email_settings_url") | your email settings - span - span(data-i18n="legal.email_description_suffix") + span.spl(data-i18n="legal.email_description_suffix") | or through links in the emails we send, | you can change your preferences and easily unsubscribe at any time. - h4(data-i18n="legal.cost_title") - | Cost - p(data-i18n="legal.cost_description") - | Currently, CodeCombat is 100% free! One of our main goals is to keep - | it that way, so that as many people can play as possible, - | regardless of place in life. If the sky darkens, - | we might have to charge subscriptions or for some content, - | but we'd rather not. With any luck, we'll be able to sustain the company with: - h4(data-i18n="legal.recruitment_title") - | Recruitment - span(data-i18n="legal.recruitment_description_prefix") - | Here on CodeCombat, you're going to become a powerful wizard–not - | just in the game, but also in real life. - span - a(href="https://code.org/stats", data-i18n="legal.url_hire_programmers") - | No one can hire programmers fast enough - span , - span(data-i18n="legal.recruitment_description_suffix") - | so once you've sharpened your skills and if you agree, - | we will demo your best coding accomplishments to the thousands of - | employers who are drooling for the chance to hire you. - | They pay us a little, they pay you - span - i(data-i18n="legal.recruitment_description_italic") - | a lot - span , - span(data-i18n="legal.recruitment_description_ending") - | the site remains free and everybody's happy. That's the plan. + if !me.get('chinaVersion') + h4(data-i18n="legal.cost_title") + | Cost + p(data-i18n="legal.cost_description") + | CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee. h2(data-i18n="legal.copyrights_title") | Copyrights and Licenses @@ -91,9 +62,8 @@ block content | Contributor License Agreement p - span(data-i18n="legal.contributor_description_prefix") + span.spr(data-i18n="legal.contributor_description_prefix") | All contributions, both on the site and on our GitHub repository, are subject to our - span a(href="/cla", data-i18n="legal.cla_url") | CLA span , @@ -104,11 +74,10 @@ block content | Code - MIT p - span(data-i18n="legal.code_description_prefix") + span.spr(data-i18n="legal.code_description_prefix") | All code owned by CodeCombat or hosted on codecombat.com, | both in the GitHub repository or in the codecombat.com database, | is licensed under the - span a(href="http://opensource.org/licenses/MIT", data-i18n="legal.mit_license_url") | MIT license span . @@ -118,14 +87,11 @@ block content h3(data-i18n="legal.art_title") | Art/Music - Creative Commons - a(rel='license', href='http://creativecommons.org/licenses/by/4.0/').cc-license-link - img(alt='Creative Commons License', style='border-width: 0; margin-left: 10px', src='http://i.creativecommons.org/l/by/4.0/88x31.png') - p - span(data-i18n="legal.art_description_prefix") + p + span.spr(data-i18n="legal.art_description_prefix") | All common content is available under the - span - a(href="http://creativecommons.org/licenses/by/4.0/", data-i18n="legal.cc_license_url") + a(href="https://creativecommons.org/licenses/by/4.0/", data-i18n="legal.cc_license_url") | Creative Commons Attribution 4.0 International License span . span(data-i18n="legal.art_description_suffix") diff --git a/app/templates/modal/contact.jade b/app/templates/modal/contact.jade deleted file mode 100644 index 7143520c2..000000000 --- a/app/templates/modal/contact.jade +++ /dev/null @@ -1,27 +0,0 @@ -extends /templates/modal/modal_base - -block modal-header-content - h3(data-i18n="contact.contact_us") Contact CodeCombat... - -block modal-body-content - p - span(data-i18n="contact.welcome") Good to hear from you! Use this form to send us email. - span(data-i18n="contact.contribute_prefix") If you're interested in contributing, check out our - a(href="/contribute", data-dismiss="modal", data-i18n="contact.contribute_page") contribute page - span(data-i18n="contact.contribute_suffix") ! - p - span(data-i18n="contact.forum_prefix") For anything public, please try - a(href="http://discourse.codecombat.com/", data-i18n="contact.forum_page") our forum - span(data-i18n="contact.forum_suffix") instead. - .form - .form-group - label.control-label(for="contact-email", data-i18n="general.email") Email - input#contact-email.form-control(name="email", type="email", value="#{me.get('anonymous') ? '' : me.get('email')}", placeholder="Where should we reply?") - .form-group - label.control-label(for="contact-message", data-i18n="general.message") Message - textarea#contact-message.form-control(name="message", rows=8) - -block modal-footer-content - span.sending-indicator.pull-left.secret(data-i18n="common.sending") Sending... - a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="common.cancel").btn Cancel - button.btn.btn-primary#contact-submit-button(data-i18n="contact.send") Send Feedback diff --git a/app/templates/modal/employer_signup_modal.jade b/app/templates/modal/employer_signup_modal.jade index 3b22f222f..9b01e3666 100644 --- a/app/templates/modal/employer_signup_modal.jade +++ b/app/templates/modal/employer_signup_modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content if userIsAnonymous || !userIsAuthorized diff --git a/app/templates/modal/job_profile_contact.jade b/app/templates/modal/job_profile_contact.jade index 412d3fca8..68d06f3c0 100644 --- a/app/templates/modal/job_profile_contact.jade +++ b/app/templates/modal/job_profile_contact.jade @@ -1,4 +1,4 @@ -extends /templates/modal/contact +extends /templates/core/contact block modal-header-content h3(data-i18n="contact.contact_candidate") Contact Candidate diff --git a/app/templates/modal/model.jade b/app/templates/modal/model-modal.jade similarity index 88% rename from app/templates/modal/model.jade rename to app/templates/modal/model-modal.jade index 404dfb571..f555ac343 100644 --- a/app/templates/modal/model.jade +++ b/app/templates/modal/model-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header diff --git a/app/templates/modal/revert.jade b/app/templates/modal/revert-modal.jade similarity index 89% rename from app/templates/modal/revert.jade rename to app/templates/modal/revert-modal.jade index 28b111337..a92ee3676 100644 --- a/app/templates/modal/revert.jade +++ b/app/templates/modal/revert-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="editor.revert_models") Revert Models diff --git a/app/templates/modal/wizard_settings.jade b/app/templates/modal/wizard_settings.jade deleted file mode 100644 index 5d7d91a6b..000000000 --- a/app/templates/modal/wizard_settings.jade +++ /dev/null @@ -1,23 +0,0 @@ -extends /templates/modal/modal_base - -block modal-header-content - h3(data-i18n="wizard_settings.title2") Customize Your Character - -block modal-body-content - form.form-horizontal - div.form-group - .row - label.control-label.col-sm-6(for="name") - | Your Wizardly Name: - #wizard-settings-name-wrapper.col-sm-4 - input#wizard-settings-name.form-control.input-sm(name="name", type="text", value="#{me.get('name')||''}") - - #wizard-settings-view - -block modal-body-wait-content - h3 Saving... -.progress.progress-striped.active - .progress-bar - -block modal-footer-content - button.btn.btn-primary.btn-large#wizard-settings-done(data-dismiss="modal", type="button") Done diff --git a/app/templates/play/campaign-view.jade b/app/templates/play/campaign-view.jade new file mode 100644 index 000000000..b1d35b33f --- /dev/null +++ b/app/templates/play/campaign-view.jade @@ -0,0 +1,147 @@ +a(href="/") + img#small-nav-logo(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat") + +if campaign + .map + .gradient.horizontal-gradient.top-gradient + .gradient.vertical-gradient.right-gradient + .gradient.horizontal-gradient.bottom-gradient + .gradient.vertical-gradient.left-gradient + .map-background(alt="", draggable="false") + + each level in levels + if !level.hidden + div(style="left: #{level.position.x}%; bottom: #{level.position.y}%; background-color: #{level.color}", class="level" + (level.next ? " next" : "") + (level.disabled ? " disabled" : "") + (level.locked ? " locked" : "") + " " + (levelStatusMap[level.slug] || ""), data-level-slug=level.slug, data-level-original=level.original, title=i18n(level, 'name') + (level.disabled ? ' (Coming Soon to Adventurers)' : '')) + if level.unlocksHero && (!level.purchasedHero || editorMode) + img.hero-portrait(src="/file/db/thang.type/#{level.unlocksHero}/portrait.png") + a(href=level.type == 'hero' ? '#' : level.disabled ? "/play" : "/play/#{level.levelPath || 'level'}/#{level.slug}", disabled=level.disabled, data-level-slug=level.slug, data-level-path=level.levelPath || 'level', data-level-name=level.name) + if level.slug == 'lost-viking' + img.star(src="/file/db/thang.type/5441c3144e9aeb727cc97111/portrait.png") + else if level.requiresSubscription + img.star(src="/images/pages/play/star.png") + if levelStatusMap[level.slug] === 'complete' + img.banner(src="/images/pages/play/level-banner-complete.png") + if levelStatusMap[level.slug] === 'started' + img.banner(src="/images/pages/play/level-banner-started.png") + if levelDifficultyMap[level.slug] + .level-difficulty-banner-text= levelDifficultyMap[level.slug] + div(style="left: #{level.position.x}%; bottom: #{level.position.y}%", class="level-shadow" + (level.next ? " next" : "") + " " + (levelStatusMap[level.slug] || "")) + .level-info-container(data-level-slug=level.slug, data-level-path=level.levelPath || 'level', data-level-name=level.name) + - var playCount = levelPlayCountMap[level.slug] + div(class="level-info " + (levelStatusMap[level.slug] || "") + (level.requiresSubscription ? " premium" : "")) + .level-status + h3= i18n(level, 'name') + (level.disabled ? " (Coming soon!)" : (level.locked ? " (Locked)" : "")) + - var description = i18n(level, 'description') || level.description || "" + .level-description!= marked(description) + if level.disabled + p + span.spr(data-i18n="play.awaiting_levels_adventurer_prefix") We release five levels per week. + a.spr(href="/contribute/adventurer") + strong(data-i18n="play.awaiting_levels_adventurer") Sign up as an Adventurer + span.spl(data-i18n="play.awaiting_levels_adventurer_suffix") to be the first to play new levels. + if level.displayConcepts && level.displayConcepts.length + p + for concept in level.displayConcepts + kbd(data-i18n="concepts." + concept) + + if !level.disabled && !level.locked + if playCount && playCount.sessions + .play-counts.hidden + span.spl.spr= playCount.sessions + span(data-i18n="play.players") players + span.spr , #{Math.round(playCount.playtime / 3600)} + span(data-i18n="play.hours_played") hours played + if levelStatusMap[level.slug] === 'complete' + button.btn.btn-warning.btn.btn-lg.btn-illustrated.view-solutions(data-level-slug=level.slug) + span(data-i18n="leaderboard.scores") + button.btn.btn-success.btn.btn-lg.btn-illustrated.start-level(data-i18n="common.play") Play + else if level.unlocksHero && !level.purchasedHero + img.hero-portrait(src="/file/db/thang.type/#{level.unlocksHero}/portrait.png", style="left: #{level.position.x}%; bottom: #{level.position.y}%;") + + for adjacentCampaign in adjacentCampaigns + a(href=(editorMode ? "/editor/campaign/" : "/play/") + adjacentCampaign.slug) + span.glyphicon.glyphicon-share-alt.campaign-switch(style=adjacentCampaign.style, title=adjacentCampaign.name, data-campaign-id=adjacentCampaign.id) + +else + .portal + .portals + for campaignSlug in ['dungeon', 'forest', 'desert', 'mountain', 'glacier', 'volcano'] + - var campaign = campaigns[campaignSlug]; + - var godmode = me.get('permissions', true).indexOf('godmode') != -1; + div(class="campaign #{campaignSlug}" + (campaign ? "" : " silhouette") + (campaign && campaign.locked && !godmode ? " locked" : ""), data-campaign-slug=campaignSlug) + .campaign-label + h2.campaign-name + if campaign + span= i18n(campaign.attributes, 'fullName') + else + span ??? + if campaign && campaign.levelsTotal + h3.levels-completed + span= campaign.levelsCompleted + | / + span= campaign.levelsTotal + if campaign && campaign.locked && !godmode + h3.campaign-locked(data-i18n="play.locked") Locked + else if campaign + btn(data-i18n="common.play").btn.btn-illustrated.btn-lg.btn-success.play-button + if campaign && campaign.get('description') + p.campaign-description + span= i18n(campaign.attributes, 'description') + +.game-controls.header-font + button.btn.poll.hidden(data-i18n="[title]play.poll") + button.btn.clans(href="/clans", data-i18n="[title]clans.clans") + button.btn.items(data-toggle='coco-modal', data-target='play/modal/PlayItemsModal', data-i18n="[title]play.items") + button.btn.heroes(data-toggle='coco-modal', data-target='play/modal/PlayHeroesModal', data-i18n="[title]play.heroes") + button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements") + if me.get('anonymous') === false || me.get('iosIdentifierForVendor') || isIPadApp + button.btn.gems(data-toggle='coco-modal', data-target='play/modal/BuyGemsModal', data-i18n="[title]play.buy_gems") + if !me.get('anonymous', true) + button.btn.account(data-toggle='coco-modal', data-target='play/modal/PlayAccountModal', data-i18n="[title]play.account") + //if me.isAdmin() + // button.btn.settings(data-toggle='coco-modal', data-target='play/modal/PlaySettingsModal', data-i18n="[title]play.settings") + if me.get('anonymous', true) + button.btn.settings(data-toggle='coco-modal', data-target='core/AuthModal', data-i18n="[title]play.settings") + +.user-status.header-font + .user-status-line + span.gem.gem-30 + span#gems-count.spr= me.gems() + span.level-indicator(data-i18n="general.player_level") + span.player-level.spr= me.level() + span.player-hero-icon + if me.get('anonymous') + span.player-name.spr(data-i18n="play.anonymous") Anonymous Player + button.btn.btn-illustrated.login-button.btn-warning(data-i18n="login.log_in") + button.btn.btn-illustrated.signup-button.btn-danger(data-i18n="signup.sign_up") + else + a(data-toggle="coco-modal", data-target="play/modal/PlayAccountModal").player-name.spr= me.get('name') + button#logout-button.btn.btn-illustrated.btn-warning(data-i18n="login.log_out") Log Out + if me.isPremium() + button.btn.btn-illustrated.btn-primary(data-i18n="nav.contact", data-toggle="coco-modal", data-target="core/ContactModal") Contact + +button.btn.btn-lg.btn-inverse.campaign-control-button#volume-button(data-i18n="[title]play.adjust_volume", title="Adjust volume") + .glyphicon.glyphicon-volume-off + .glyphicon.glyphicon-volume-down + .glyphicon.glyphicon-volume-up + +if campaign && !editorMode + button.btn.btn-lg.btn-inverse.campaign-control-button#back-button(data-i18n="[title]resources.campaigns", title="Campaigns") + .glyphicon.glyphicon-globe + +if editorMode + button.btn.btn-lg.btn-inverse.campaign-control-button#clear-storage-button(data-i18n="[title]editor.clear_storage", title="Clear your local changes") + .glyphicon.glyphicon-refresh + +if campaign && campaign.loaded + h1#campaign-status + .campaign-status-background + .campaign-name + - var fullName = i18n(campaign.attributes, 'fullName') + if (me.get('preferredLanguage', true) || 'en-US').split('-')[0] == 'en' || fullName != campaign.get('fullName') + // We have a translation. + span= fullName + .levels-completed + span= levelsCompleted + | / + span= levelsTotal diff --git a/app/templates/play/common/ladder_submission.jade b/app/templates/play/common/ladder_submission.jade index dc387bc62..d909ccf9e 100644 --- a/app/templates/play/common/ladder_submission.jade +++ b/app/templates/play/common/ladder_submission.jade @@ -1,4 +1,4 @@ -button.btn.btn-lg.btn-block.btn-success.rank-button +button.btn.btn-lg.btn-block.btn-success.rank-button.btn-illustrated span(data-i18n="ladder.rank_no_code").unavailable.secret No New Code to Rank span(data-i18n="ladder.rank_my_game").rank.secret Rank My Game! span(data-i18n="ladder.rank_submitting").submitting.secret Submitting... diff --git a/app/templates/play/ladder/ladder_tab.jade b/app/templates/play/ladder/ladder-tab-view.jade similarity index 76% rename from app/templates/play/ladder/ladder_tab.jade rename to app/templates/play/ladder/ladder-tab-view.jade index 9a743ab6f..23be6e709 100644 --- a/app/templates/play/ladder/ladder_tab.jade +++ b/app/templates/play/ladder/ladder-tab-view.jade @@ -1,19 +1,21 @@ div#columns.row for team, teamIndex in teams div.column.col-md-4 - div(id="histogram-display-#{team.name}", class="histogram-display",data-team-name=team.name) - table.table.table-bordered.table-condensed.table-hover + div(id="histogram-display-#{team.name}", class="histogram-display", data-team-name=team.name) + table.table.table-bordered.table-condensed.table-hover(data-team=team.id) thead tr - th(colspan=2) - th(colspan=3, style="color: #{team.primaryColor}") + th(colspan=level.get('type', true) == 'hero-ladder' ? 3 : 2) + th(colspan=4, style="color: #{team.primaryColor}") span= team.name span.spl(data-i18n="ladder.leaderboard") Leaderboard tr - th(colspan=2) + th(colspan=level.get('type', true) == 'hero-ladder' ? 3 : 2) th(data-i18n="general.score") Score th(data-i18n="general.name").name-col-cell Name th + th.iconic-cell + .glyphicon.glyphicon-eye-open tbody - var topSessions = team.leaderboard.topPlayers.models; @@ -23,12 +25,16 @@ div#columns.row - var myRow = session.get('creator') == me.id tr(class=myRow ? "success" : "", data-player-id=session.get('creator'), data-session-id=session.id) td.code-language-cell(style="background-image: url(/images/common/code_languages/" + session.get('submittedCodeLanguage') + "_icon.png)" title=capitalize(session.get('submittedCodeLanguage'))) + if level.get('type', true) == 'hero-ladder' + td.hero-portrait-cell(style="background-image: url(/file/db/thang.type/#{(session.get('heroConfig') || {}).thangType || '529ffbf1cf1818f2be000001'}/portrait.png)") td.rank-cell= rank + 1 td.score-cell= Math.round(session.get('totalScore') * 100) td.name-col-cell= session.get('creatorName') || "Anonymous" td.fight-cell a(href="/play/level/#{level.get('slug') || level.id}?team=#{team.otherTeam}&opponent=#{session.id}") span(data-i18n="ladder.fight") Fight! + td.spectate-cell.iconic-cell + .glyphicon.glyphicon-eye-open if !showJustTop && team.leaderboard.nearbySessions().length tr(class="active") @@ -37,14 +43,18 @@ div#columns.row - var myRow = session.get('creator') == me.id tr(class=myRow ? "success" : "", data-player-id=session.get('creator'), data-session-id=session.id) td.code-language-cell(style="background-image: url(/images/common/code_languages/" + session.get('submittedCodeLanguage') + "_icon.png)") + if level.get('type', true) == 'hero-ladder' + td.hero-portrait-cell(style="background-image: url(/file/db/thang.type/#{(session.get('heroConfig') || {}).thangType || '529ffbf1cf1818f2be000001'}/portrait.png)") td.rank-cell= session.rank td.score-cell= Math.round(session.get('totalScore') * 100) td.name-col-cell= session.get('creatorName') || "Anonymous" td.fight-cell a(href="/play/level/#{level.get('slug') || level.id}?team=#{team.otherTeam}&opponent=#{session.id}") span(data-i18n="ladder.fight") Fight! + td.spectate-cell.iconic-cell + .glyphicon.glyphicon-eye-open if teamIndex == 1 - .btn.btn-sm.load-more-ladder-entries More + .btn.btn-sm.load-more-ladder-entries(data-i18n="editor.more") More div.column.col-md-4 h4.friends-header(data-i18n="ladder.friends_playing") Friends Playing diff --git a/app/templates/play/ladder/ladder.jade b/app/templates/play/ladder/ladder.jade index f0e07073d..b92891c08 100644 --- a/app/templates/play/ladder/ladder.jade +++ b/app/templates/play/ladder/ladder.jade @@ -2,88 +2,111 @@ extends /templates/base block content - var base = "/images/pages/play/ladder/prize_"; - div#level-column - if levelDescription - div!= levelDescription - else - h1= level.get('name') + div#ladder-top - if level.get('name') == 'Greed' - .tournament-blurb - h2 - span(data-i18n="ladder.tournament_ended") Tournament ended - | #{tournamentTimeLeft} - p - span(data-i18n="ladder.tournament_blurb") Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details - | - a(href="http://blog.codecombat.com/multiplayer-programming-tournament", data-i18n="ladder.tournament_blurb_blog") on our blog - | . - p - strong Tournament ended! - a(href="#winners") Behold the winners - | . Thanks for playing! You can - strong still play - | Greed and all of our other - a(href="/play/ladder") multiplayer arenas - | . - p - | Want to commiserate? Head over to - a(href="http://discourse.codecombat.com/") the forum - | and discuss your strategies, your triumphs, and your turmoils. + div#level-column + if levelDescription + div!= levelDescription + else + h1= level.get('name') - .sponsor-logos - a(href="https://heapanalytics.com/") - img(src=base + "heap.png") - a(href="https://www.firebase.com/") - img(src=base + "firebase.png") - a(href="https://onemonthrails.com/") - img(src=base + "one_month.png") - a(href="http://www.jetbrains.com/webstorm/") - img(src=base + "webstorm.png") - a(href="http://shop.oreilly.com/category/ebooks.do") - img(src=base + "oreilly.png") - a(href="http://aws.amazon.com/") - img(src=base + "aws.png") - - if level.get('name') == 'Criss-Cross' - .tournament-blurb - h2 - span(data-i18n="ladder.tournament_ended") Tournament ended - | #{tournamentTimeLeft} - p - span(data-i18n="ladder.tournament_blurb_criss_cross") Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details - | - a(href="http://blog.codecombat.com/6-programming-languages-one-victor-codecombats-newest-tournament", data-i18n="ladder.tournament_blurb_blog") on our blog - | . - p - strong Tournament ended! - a(href="#winners") Behold the winners - | . Thanks for playing! You can - strong still play - | Criss-Cross and all of our other - a(href="/play/ladder") multiplayer arenas - | . - p - | Want to commiserate? Head over to - a(href="http://discourse.codecombat.com/") the forum - | and discuss your strategies, your triumphs, and your turmoils. - - div#columns.row - div.column.col-md-2 - for team in teams - div.column.col-md-4 - a(style="background-color: #{team.primaryColor}", data-team=team.id).play-button.btn.btn-danger.btn-block.btn-lg - span(data-i18n="play.play_as") Play As + if level.get('name') == 'Greed' + .tournament-blurb + h2 + span(data-i18n="ladder.tournament_ended") Tournament ended + | #{tournamentTimeLeft} + p + span(data-i18n="ladder.tournament_blurb") Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details | - span= team.name - div.column.col-md-2 - - .spectate-button-container - a(href="/play/spectate/#{level.get('slug')}").spectate-button.btn.btn-primary.center - span(data-i18n="play.spectate") Spectate + a(href="http://blog.codecombat.com/multiplayer-programming-tournament", data-i18n="ladder.tournament_blurb_blog") on our blog + | . + p + strong Tournament ended! + a(href="#winners") Behold the winners + | . Thanks for playing! You can + strong still play + | Greed and all of our other + a(href="/play/ladder") multiplayer arenas + | . + p + | Want to commiserate? Head over to + a(href="http://discourse.codecombat.com/") the forum + | and discuss your strategies, your triumphs, and your turmoils. + + .sponsor-logos + a(href="https://heapanalytics.com/") + img(src=base + "heap.png") + a(href="https://www.firebase.com/") + img(src=base + "firebase.png") + a(href="https://onemonthrails.com/") + img(src=base + "one_month.png") + a(href="http://www.jetbrains.com/webstorm/") + img(src=base + "webstorm.png") + a(href="http://shop.oreilly.com/category/ebooks.do") + img(src=base + "oreilly.png") + a(href="http://aws.amazon.com/") + img(src=base + "aws.png") + + if level.get('name') == 'Criss-Cross' + .tournament-blurb + h2 + span(data-i18n="ladder.tournament_ended") Tournament ended + | #{tournamentTimeLeft} + p + span(data-i18n="ladder.tournament_blurb_criss_cross") Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details + | + a(href="http://blog.codecombat.com/6-programming-languages-one-victor-codecombats-newest-tournament", data-i18n="ladder.tournament_blurb_blog") on our blog + | . + p + strong Tournament ended! + a(href="#winners") Behold the winners + | . Thanks for playing! You can + strong still play + | Criss-Cross and all of our other + a(href="/play/ladder") multiplayer arenas + | . + p + | Want to commiserate? Head over to + a(href="http://discourse.codecombat.com/") the forum + | and discuss your strategies, your triumphs, and your turmoils. + + if level.get('name') == 'Zero Sum' + .tournament-blurb + h2 + span(data-i18n="ladder.tournament_ended") Tournament ended + | #{tournamentTimeLeft} + //span(data-i18n="ladder.tournament_started") , started + //| #{tournamentTimeElapsed} + p + span(data-i18n="ladder.tournament_blurb_zero_sum") Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details + | + a(href="http://blog.codecombat.com/zero-sum", data-i18n="ladder.tournament_blurb_blog") on our blog + | . + p + strong Tournament ended! + a(href="#winners") Behold the winners + | . Thanks for playing! You can + strong still play + | Zero Sum as long as you like. + p + | Want to commiserate? Head over to + a(href="http://discourse.codecombat.com/") the forum + | and discuss your strategies, your triumphs, and your turmoils. + + div#columns.row + div.column.col-md-2 + for team in teams + div.column.col-md-4 + a(class="play-button btn btn-illustrated btn-block btn-lg " + (team.id == 'ogres' ? 'btn-primary' : 'btn-danger'), data-team=team.id) + span(data-i18n="play.play_as") Play As + | + span= team.name + div.column.col-md-2 + + .spectate-button-container + a(href="/play/spectate/#{level.get('slug')}").spectate-button.btn.btn-illustrated.btn-info.center + span(data-i18n="play.spectate") Spectate - hr - ul.nav.nav-pills li.active a(href="#ladder", data-toggle="tab", data-i18n="general.ladder") Ladder @@ -98,7 +121,7 @@ block content if level.get('name') == 'Greed' li a(href="#rules", data-toggle="tab", data-i18n="ladder.rules") Rules - if level.get('name') == 'Greed' || (level.get('name') == 'Criss-Cross') + if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum' li a(href="#winners", data-toggle="tab", data-i18n="ladder.winners") Winners @@ -737,7 +760,7 @@ block content a(href="http://discourse.codecombat.com/") Discourse forum | . - if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross' + if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum' .tab-pane.well#winners h1(data-i18n="ladder.winners") Winners @@ -745,17 +768,19 @@ block content thead tr th(data-i18n="ladder_prizes.rank") Rank - if level.get('name') == 'Criss-Cross' + if level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum' th th Human - if level.get('name') == 'Greed' + if level.get('name') == 'Greed' || level.get('name') == 'Zero Sum' th Human wins/losses/ties else th Human score - if level.get('name') == 'Criss-Cross' + if level.get('name') == 'Zero Sum' + th + if level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum' th th Ogre - if level.get('name') == 'Greed' + if level.get('name') == 'Greed' || level.get('name') == 'Zero Sum' th Ogre wins/losses/ties else th Ogre score @@ -765,30 +790,38 @@ block content - var ogre = winners.ogres[index] tr td= human.rank - if level.get('name') == 'Criss-Cross' + if level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum' td.code-language-cell(style="background-image: url(/images/common/code_languages/" + human.codeLanguage + "_icon.png)" title=_.string.capitalize(human.codeLanguage)) td= human.name - if level.get('name') == 'Greed' + if level.get('name') == 'Greed' || level.get('name') == 'Zero Sum' td span.win= human.wins | - span.loss= human.losses | - - span.tie= 377 - human.wins - human.losses + if level.get('name') == 'Greed' + span.tie= 377 - human.wins - human.losses + else if level.get('name') == 'Zero Sum' + span.tie= 108 - human.wins - human.losses else td span= Math.round(100 * human.score) if ogre - if level.get('name') == 'Criss-Cross' + if level.get('name') == 'Zero Sum' + td= ogre.rank + if level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum' td.code-language-cell(style="background-image: url(/images/common/code_languages/" + ogre.codeLanguage + "_icon.png)" title=_.string.capitalize(ogre.codeLanguage)) td= ogre.name - if level.get('name') == 'Greed' + if level.get('name') == 'Greed' || level.get('name') == 'Zero Sum' td span.win= ogre.wins | - span.loss= ogre.losses | - - span.tie= 407 - ogre.wins - ogre.losses + if level.get('name') == 'Greed' + span.tie= 407 - ogre.wins - ogre.losses + else if level.get('name') == 'Zero Sum' + span.tie= Math.max(0, 163 - ogre.wins - ogre.losses) else td span= Math.round(100 * ogre.score) diff --git a/app/templates/play/ladder/my_matches_tab.jade b/app/templates/play/ladder/my_matches_tab.jade index bf4d9e856..5c1a12543 100644 --- a/app/templates/play/ladder/my_matches_tab.jade +++ b/app/templates/play/ladder/my_matches_tab.jade @@ -31,7 +31,7 @@ div#columns.row th(data-i18n="general.when") When th for match in team.matches - tr(class=(match.stale ? "stale " : "") + (match.fresh ? "fresh " : "") + match.state) + tr(class=(match.stale ? "stale " : "") + (match.fresh ? "fresh " : "") + match.state, title=match.simulator) td.state-cell if match.state === 'win' span(data-i18n="general.win").win Win diff --git a/app/templates/play/ladder/play_modal.jade b/app/templates/play/ladder/play_modal.jade index 459d7ec9e..86631c841 100644 --- a/app/templates/play/ladder/play_modal.jade +++ b/app/templates/play/ladder/play_modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="ladder.choose_opponent") Choose an Opponent diff --git a/app/templates/play/ladder_home.jade b/app/templates/play/ladder_home.jade index 4a12d7683..ec2df7906 100644 --- a/app/templates/play/ladder_home.jade +++ b/app/templates/play/ladder_home.jade @@ -14,7 +14,7 @@ block content img.level-image(src="#{level.image}", alt="#{level.name}").img-rounded else img.level-image(src="/images/pages/play/ladder/multiplayer_notext.jpg", alt="#{level.name}").img-rounded - //h3= level.name + (level.disabled ? " (Coming soon!)" : "") + h3.dynamic-level-name= level.name + (level.disabled ? " (Coming soon!)" : "") .overlay-text.level-difficulty span(data-i18n="play.level_difficulty") Difficulty: each i in Array(level.difficulty) @@ -24,4 +24,4 @@ block content span.spl.spr - #{playCount.sessions} span(data-i18n="play.players") players .play-text-container - .overlay-text.play-text(data-i18n="home.play") Play + .overlay-text.play-text(data-i18n="common.play") Play diff --git a/app/templates/play/level.jade b/app/templates/play/level.jade index d9bccdf51..9b8dca111 100644 --- a/app/templates/play/level.jade +++ b/app/templates/play/level.jade @@ -4,36 +4,44 @@ #control-bar-view #fullscreen-editor-background-screen(title="Click to minimize the code editor") - + #code-area #code-area-gradient.gradient #tome-view - - #canvas-wrapper - canvas(width=924, height=589)#webgl-surface - canvas(width=924, height=589)#normal-surface - #canvas-left-gradient.gradient - #canvas-top-gradient.gradient - #goals-view - - #level-flags-view - #gold-view + #game-area - #problem-alert-view + #canvas-wrapper + canvas(width=924, height=589)#webgl-surface + canvas(width=924, height=589)#normal-surface + #ascii-surface + #canvas-left-gradient.gradient + #canvas-top-gradient.gradient + #goals-view - #level-chat-view - - #multiplayer-status-view - - #playback-view - - #thang-hud + #level-flags-view - #level-dialogue-view + #gold-view + + #problem-alert-view + + #level-chat-view + + #multiplayer-status-view + + #playback-view + + #thang-hud + + #level-dialogue-view button.btn.btn-lg.btn-warning.banner.header-font#stop-real-time-playback-button(title="Stop real-time playback", data-i18n="play_level.skip") Skip -#play-footer - p(class='footer-link-text') - a(title='Send CodeCombat a message', tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="nav.contact") Contact +#level-footer-shadow +#level-footer-background + +if !me.get('anonymous') + #play-footer(class=me.isPremium() ? "premium" : "") + p(class='footer-link-text') + a.contact-link(title='Send CodeCombat a message', tabindex=-1, data-i18n="nav.contact") Contact + diff --git a/app/templates/play/level/control_bar.jade b/app/templates/play/level/control_bar.jade index e03a3ab52..176778657 100644 --- a/app/templates/play/level/control_bar.jade +++ b/app/templates/play/level/control_bar.jade @@ -9,7 +9,7 @@ .glyphicon.glyphicon-play span(data-i18n="nav.play").home-text Levels -if isMultiplayerLevel +if isMultiplayerLevel && !observing .multiplayer-area-container .multiplayer-area .multiplayer-label(data-i18n="play_level.control_bar_multiplayer") @@ -21,21 +21,26 @@ else .level-name-area-container .level-name-area .level-label(data-i18n="play_level.level") - .level-name= worldName + .level-name(title=difficultyTitle || "") + span= worldName + if levelDifficulty + sup.level-difficulty= levelDifficulty .buttons-area - button.btn.btn-inverse#game-menu-button(title="Show game menu") - .hamburger - span.icon-bar - span.icon-bar - span.icon-bar - span.game-menu-text(data-i18n="play_level.game_menu") Game Menu + if !observing + button.btn.btn-inverse#game-menu-button(title="Show game menu") + .hamburger + span.icon-bar + span.icon-bar + span.icon-bar + span.game-menu-text(data-i18n="play_level.game_menu") Game Menu if spectateGame button.btn.btn-xs.btn-inverse.banner#next-game-button(title="Next Game", data-i18n="play_level.next-game") Next game! - button.btn.btn-xs.btn-primary.banner#level-done-button(data-i18n="play_level.done") Done + if !observing + button.btn.btn-xs.btn-primary.banner#level-done-button(data-i18n="play_level.done") Done if me.get('anonymous') button.btn.btn-xs.btn-primary.banner#control-bar-sign-up-button(data-toggle='coco-modal', data-target='core/AuthModal', data-i18n="signup.sign_up") diff --git a/app/templates/play/level/level_loading.jade b/app/templates/play/level/level_loading.jade index 0ef0f2afa..236f07d69 100644 --- a/app/templates/play/level/level_loading.jade +++ b/app/templates/play/level/level_loading.jade @@ -4,15 +4,32 @@ .loading-details.loading-container - .load-progress - .progress - .progress-bar.progress-bar-success + .level-loading-goals.secret + .goals-title(data-i18n="play_level.goals") Goals + ul.list-unstyled + + .errors + + .progress-or-start-container + button.start-level-button.btn.btn-lg.btn-success.btn-illustrated.header-font.needsclick(data-i18n="play_level.loading_start") Start Level + + .load-progress + .progress + .progress-background + .progress-bar-container + .progress-bar.progress-bar-success + .rim + + .subscription-required + span(data-i18n="subscribe.subscription_required_to_play") You'll need a subscription to play this level. + button.start-subscription-button.btn.btn-lg.btn-warning(data-i18n="subscribe.subscribe") Subscribe #tip-wrapper strong.tip(data-i18n='play_level.tip_toggle_play') Toggle play/paused with Ctrl+P. strong.tip(data-i18n='play_level.tip_scrub_shortcut') Ctrl+[ and Ctrl+] rewind and fast-forward. strong.tip(data-i18n='play_level.tip_guide_exists') Click the guide, inside game menu (at the top of the page), for useful info. strong.tip(data-i18n='play_level.tip_open_source') CodeCombat is 100% open source! + strong.tip(data-i18n='play_level.tip_tell_friends') Enjoying CodeCombat? Tell your friends about us! strong.tip(data-i18n='play_level.tip_beta_launch') CodeCombat launched its beta in October, 2013. strong.tip(data-i18n='play_level.tip_think_solution') Think of the solution, not the problem. strong.tip(data-i18n='play_level.tip_theory_practice') In theory there is no difference between theory and practice; in practice there is. - Yogi Berra @@ -20,6 +37,8 @@ strong.tip(data-i18n='play_level.tip_debugging_program') If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra strong.tip(data-i18n='play_level.tip_forums') Head over to the forums and tell us what you think! strong.tip(data-i18n='play_level.tip_impossible') It always seems impossible until it's done. - Nelson Mandela + strong.tip(data-i18n='play_level.tip_move_forward') Whatever you do, keep moving forward. - Martin Luther King Jr. + strong.tip(data-i18n='play_level.tip_google') Have a problem you can't solve? Google it! strong.tip.rare(data-i18n='play_level.tip_baby_coders') In the future, even babies will be Archmages. strong.tip.rare(data-i18n='play_level.tip_hardware_problem') Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem. @@ -39,14 +58,23 @@ strong.tip.rare(data-i18n='play_level.tip_premature_optimization') Premature optimization is the root of all evil - Donald Knuth strong.tip.rare(data-i18n='play_level.tip_brute_force') When in doubt, use brute force. - Ken Thompson strong.tip.rare(data-i18n='play_level.tip_extrapolation') There are only two kinds of people: those that can extrapolate from incomplete data... + strong.tip.rare(data-i18n='play_level.tip_superpower') Coding is the closest thing we have to a superpower + strong.tip.rare(data-i18n='play_level.tip_source_code') I want to change the world but they would not give me the source code. + strong.tip.rare(data-i18n='play_level.tip_javascript_java') Java is to JavaScript what Car is to Carpet. - Chris Heilmann strong.tip.rare span(data-i18n='play_level.tip_harry') Yer a Wizard, span= me.get('name', true) - - .errors - - .panel.panel-default.level-loading-goals.secret - .panel-heading.header-font(data-i18n="play_level.goals") Goals - ul.list-group - - button.start-level-button.btn.btn-lg.btn-success.header-font.secret.needsclick(data-i18n="play_level.loading_start") Start Level + strong.tip.rare(data-i18n='play_level.tip_control_destiny') In real open source, you have the right to control your own destiny. - Linus Torvalds + strong.tip.rare(data-i18n='play_level.tip_no_code') No code is faster than no code. + strong.tip.rare(data-i18n='play_level.tip_code_never_lies') Code never lies, comments sometimes do. — Ron Jeffries + strong.tip.rare(data-i18n='play_level.tip_reusable_software') Before software can be reusable it first has to be usable. + strong.tip.rare(data-i18n='play_level.tip_optimization_operator') Every language has an optimization operator. In most languages that operator is ‘//’ + strong.tip.rare(data-i18n='play_level.tip_lines_of_code') Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates + strong.tip.rare(data-i18n='play_level.tip_adding_evil') Adding a pinch of evil. + strong.tip.rare(data-i18n='play_level.tip_hate_computers') That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven + strong.tip.rare + a(href="https://github.com/codecombat/codecombat/wiki", data-i18n='play_level.tip_open_source_contribute') You can help CodeCombat improve! + strong.tip.rare(data-i18n='play_level.tip_recurse') To iterate is human, to recurse divine. - L. Peter Deutsch + strong.tip.rare(data-i18n='play_level.tip_free_your_mind') You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus + strong.tip.rare(data-i18n='play_level.tip_strong_opponents') Even the strongest of opponents always has a weakness. - Itachi Uchiha + strong.tip.rare(data-i18n='play_level.tip_paper_and_pen') Before you start coding, you can always plan with a sheet of paper and a pen. diff --git a/app/templates/play/level/modal/hero-victory-modal.jade b/app/templates/play/level/modal/hero-victory-modal.jade index 5302dc502..2412c10e2 100644 --- a/app/templates/play/level/modal/hero-victory-modal.jade +++ b/app/templates/play/level/modal/hero-victory-modal.jade @@ -1,30 +1,54 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content - img(src="/images/pages/play/level/modal/victory_modal_blue_banner.png")#victory-banner - img(src="/images/pages/play/level/modal/victory_word.png")#victory-header.out + #victory-header.out + #victory-title + if !me.get('preferredLanguage') || me.get('preferredLanguage').split('-')[0] == 'en' + img(src="/images/pages/play/level/modal/victory_word.png", draggable="false") + else + h1(data-i18n="play_level.victory") Victory block modal-body-content - + if victoryText + #victory-text= victoryText + + #level-feedback + div.rating.secret + div.rating-label(data-i18n="play_level.victory_rate_the_level") Rate the level: + i.glyphicon.glyphicon-star-empty + i.glyphicon.glyphicon-star-empty + i.glyphicon.glyphicon-star-empty + i.glyphicon.glyphicon-star-empty + i.glyphicon.glyphicon-star-empty + if !me.get('anonymous') + span.review-label.secret(data-i18n="play_level.victory_review") Tell us more! + .review.secret + br + textarea(data-i18n="[placeholder]play_level.victory_review_placeholder") + .clearfix + for achievement in achievements - var animate = achievement.completed && !achievement.completedAWhileAgo .achievement-panel(class=achievement.completedAWhileAgo ? 'earned' : '' data-achievement-id=achievement.id data-animate=animate) - var rewards = achievement.get('rewards') || {}; - div.achievement-description= achievement.get('description') + div.achievement-description= achievement.description div.achievement-rewards - - var worth = achievement.get('worth', true); + - var worth = achievement.worth; + - var previousWorth = achievement.previousWorth; + - var gems = achievement.gems; + - var previousGems = achievement.previousGems; if worth - .reward-panel.numerical.xp(data-number=worth, data-number-unit='xp') + .reward-panel.numerical.xp(data-number=worth, data-number-unit='xp', data-previous-number=previousWorth || 0) .reward-image-container(class=animate ? 'pending-reward-image' : 'show') img(src="/images/pages/play/level/modal/reward_icon_xp.png") .reward-text= animate ? '+0' : '+'+worth - if rewards.gems - .reward-panel.numerical.gems(data-number=rewards.gems, data-number-unit='gem') + if gems + .reward-panel.numerical.gems(data-number=gems, data-number-unit='gem', data-previous-number=previousGems || 0) .reward-image-container(class=animate ? 'pending-reward-image' : 'show') img(src="/images/pages/play/level/modal/reward_icon_gems.png") - .reward-text= animate ? '+0' : '+'+rewards.gems + .reward-text= animate ? '+0' : '+'+gems if rewards.heroes for hero in rewards.heroes @@ -40,41 +64,59 @@ block modal-body-content .reward-panel.item(data-item-thang-type=item.get('original')) .reward-image-container(class=animate ? 'pending-reward-image' : 'show') img(src=item.getPortraitURL()) - .reward-text= animate ? 'New Item' : item.get('name') - - .next-levels-prompt - for button in continueButtons - - var enabled = Boolean(button.link != '/play' || me.getBranchingGroup() == 'choice-implicit' || button.key == 'continue'); - button.btn.btn-success.btn-lg.world-map-button.next-level-branch-button(data-href=button.link, disabled=!enabled, data-dismiss="modal", data-i18n="play_level.victory_play_" + button[me.getBranchingGroup()], data-branch-key=button.key) + if animate + .reward-text(data-i18n="play_level.victory_new_item") New Item + else + .reward-text= i18n(item.attributes, 'name') block modal-footer-content + #totals + .total-wrapper#xp-wrapper + .total-count#xp-total 0 + .total-label + span.spr(data-i18n="play_level.victory_experience_gained") XP Gained + | - + span.spl.spr(data-i18n="general.player_level") Level + span.level= me.level() + .xp-bar-outer + .xp-bar-already-achieved + .xp-bar-total + .total-wrapper#gem-wrapper + .total-count#gem-total 0 + .total-label(data-i18n="play_level.victory_gems_gained") Gems Gained + if me.get('anonymous') - p.sign-up-poke.hide - button.btn.btn-success.sign-up-button.btn-large(data-toggle="coco-modal", data-target="modal/SignupModal", data-i18n="play_level.victory_sign_up") Sign Up to Save Progress - span(data-i18n="play_level.victory_sign_up_poke") Want to save your code? Create a free account! + .sign-up-poke.hide + .sign-up-blurb(data-i18n="play_level.victory_sign_up_poke") Want to save your code? Create a free account! + button.btn.btn-illustrated.btn-warning.sign-up-button.btn-lg(data-dismiss="modal", data-i18n="play_level.victory_sign_up") Sign Up to Save Progress - div#totals.pull-left - span.spr Experience Gained: - span#xp-total +0 - br - span.spr Gems Gained: - span#gem-total +0 - - button.btn.btn-warning.hide#saving-progress-label(disabled, data-i18n="play_level.victory_saving_progress") Saving Progress + button.btn.btn-illustrated.btn-lg.btn-warning.hide#saving-progress-label(disabled, data-i18n="play_level.victory_saving_progress") Saving Progress .next-level-buttons if readyToRank .ladder-submission-view else if level.get('type') === 'hero-ladder' - button.btn.btn-primary.return-to-ladder-button(data-href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_return_to_ladder") Return to Ladder + button.btn.btn-illustrated.btn-primary.btn-lg.return-to-ladder-button(data-href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_return_to_ladder") Return to Ladder else - button.btn.btn-success.world-map-button.next-level-button.hide#continue-button(data-i18n="play_level.victory_play_continue") Continue + button.btn.btn-illustrated.btn-success.btn-lg.world-map-button.next-level-button.hide#continue-button(data-i18n="play_level.victory_play_continue") Continue + + if !me.get('anonymous') && !showHourOfCodeDoneButton && showLeaderboard + button.btn.btn-illustrated.btn-success.leaderboard-button.btn-lg(data-dismiss="modal", data-i18n="leaderboard.view_other_solutions") View Other Solutions + else if showReturnToCourse + button.btn.btn-illustrated.btn-warning.return-to-course-button.btn-lg(data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home if showHourOfCodeDoneButton .hour-of-code-done - hr a.image-link(href="http://code.org/api/hour/finish") img(src="/images/level/csedweek-logo-final-small.jpg", alt="CS Ed Week Hour of Code", title="I'm finished with my Hour of Code", width=80) strong(data-i18n="play_level.victory_hour_of_code_done") Are You Done? a.text-link(href="http://code.org/api/hour/finish") - span(data-i18n="play_level.victory_hour_of_code_done_yes") Yes, I'm finished with my Hour of Code! + span(data-i18n="play_level.victory_hour_of_code_done_yes") Yes, I am finished with my Hour of Code! + .clearfix + + .offer.lost-viking + p + img.pull-left(src="/file/db/level/55144b509f0c4854051769c1/viking1.png") + img.pull-right(src="/file/db/level/55144b509f0c4854051769c1/viking_2.png") + span(data-i18n="play_level.victory_viking_code_school") + button.btn.btn-illustrated.btn-primary.btn-lg.world-map-button.continue-from-offer-button(data-i18n="play_level.victory_become_a_viking") Become a Viking diff --git a/app/templates/play/level/modal/infinite_loop.jade b/app/templates/play/level/modal/infinite_loop.jade index b2128482b..ef439d196 100644 --- a/app/templates/play/level/modal/infinite_loop.jade +++ b/app/templates/play/level/modal/infinite_loop.jade @@ -1,12 +1,19 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content - h3(data-i18n="play_level.infinite_loop_title") Infinite Loop Detected + if nonUserCodeProblem + h3(data-i18n="play_level.non_user_code_problem_title") Unable to Load Level + else + h3(data-i18n="play_level.infinite_loop_title") Infinite Loop Detected block modal-body-content .modal-body p(data-i18n="play_level.infinite_loop_explanation") The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know. + p + span.spr(data-i18n="play_level.check_dev_console") You can also open the developer console to see what might be going wrong. + a(href="http://webmasters.stackexchange.com/questions/8525/how-to-open-the-javascript-console-in-different-browsers/77337#77337", data-i18n="play_level.check_dev_console_instructions", target="_blank") (instructions) + block modal-footer-content a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="play_level.infinite_loop_try_again").btn#restart-level-infinite-loop-retry-button Try Again a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="play_level.infinite_loop_reset_level").btn.btn-danger#restart-level-infinite-loop-confirm-button Reset Level diff --git a/app/templates/play/level/modal/keyboard_shortcuts.jade b/app/templates/play/level/modal/keyboard_shortcuts.jade index 961b595d1..6a223b5f5 100644 --- a/app/templates/play/level/modal/keyboard_shortcuts.jade +++ b/app/templates/play/level/modal/keyboard_shortcuts.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="keyboard_shortcuts.keyboard_shortcuts") Keyboard Shortcuts @@ -74,16 +74,6 @@ block modal-body-content dt(title=ctrlName + " " + shift + " M") kbd #{ctrl} ⇧ M dd(data-i18n="keyboard_shortcuts.maximize_editor") Maximize/minimize code editor. - dl.dl-horizontal - dt(title="Arrow keys") - kbd ← - | , - kbd → - | , - kbd ↑ - | , - kbd ↓ - dd(data-i18n="keyboard_shortcuts.move_wizard") Move your Wizard around the level. block modal-footer-content a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="modal.close").btn.btn-primary Close diff --git a/app/templates/play/level/modal/reload-level-modal.jade b/app/templates/play/level/modal/reload-level-modal.jade index 5992717e1..6dae685ff 100644 --- a/app/templates/play/level/modal/reload-level-modal.jade +++ b/app/templates/play/level/modal/reload-level-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="play_level.reload_title") Reload All Code? diff --git a/app/templates/play/level/modal/victory.jade b/app/templates/play/level/modal/victory.jade index ae8621fb5..effaad52c 100644 --- a/app/templates/play/level/modal/victory.jade +++ b/app/templates/play/level/modal/victory.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3 @@ -15,15 +15,11 @@ block modal-footer-content .ladder-submission-view else if level.get('type') === 'ladder' a.btn.btn-primary(href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_return_to_ladder") Return to Ladder - else if level.get('type', true) === 'hero' - a.btn.btn-success.world-map-button(href="/play-hero", data-dismiss="modal", data-i18n="play_level.victory_play_continue") Continue - else if hasNextLevel - button.btn.btn-success.next-level-button(data-dismiss="modal", data-i18n="play_level.victory_play_next_level") Play Next Level else a.btn.btn-primary(href="/", data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home if me.get('anonymous') p.sign-up-poke - button.btn.btn-success.sign-up-button.btn-large(data-toggle="coco-modal", data-target="modal/SignupModal", data-i18n="play_level.victory_sign_up") Sign Up to Save Progress + button.btn.btn-success.sign-up-button.btn-large(data-dismiss="modal", data-i18n="play_level.victory_sign_up") Sign Up to Save Progress span(data-i18n="play_level.victory_sign_up_poke") Want to save your code? Create a free account! p.clearfix else @@ -43,10 +39,4 @@ block modal-footer-content .g-plusone(data-href="http://codecombat.com", data-size="medium") .fb-like(data-href="https://www.facebook.com/codecombat", data-send="false", data-layout="button_count", data-width="350", data-show-faces="true", data-ref="coco_victory_#{fbRef}") a.twitter-follow-button(href="https://twitter.com/CodeCombat", data-show-count="true", data-show-screen-name="false", data-dnt="true", data-align="right", data-i18n="nav.twitter_follow") Follow - iframe.github-star-button(src="http://ghbtns.com/github-btn.html?user=codecombat&repo=codecombat&type=watch&count=true", allowtransparency="true", frameborder="0", scrolling="0", width="110", height="20") - - if showHourOfCodeDoneButton - h3.pull-left(data-i18n="play_level.victory_hour_of_code_done") Are You Done? - a(href="http://code.org/api/hour/finish") - strong(data-i18n="play_level.victory_hour_of_code_done_yes") Yes, I'm finished with my Hour of Code! - img(src="/images/level/csedweek-logo-final-small.jpg", alt="CS Ed Week Hour of Code", title="I'm finished with my Hour of Code", width=80) + iframe.github-star-button(src="https://ghbtns.com/github-btn.html?user=codecombat&repo=codecombat&type=watch&count=true", allowtransparency="true", frameborder="0", scrolling="0", width="110", height="20") diff --git a/app/templates/play/level/tome/cast_button.jade b/app/templates/play/level/tome/cast_button.jade index 77886e0df..a8f2470cd 100644 --- a/app/templates/play/level/tome/cast_button.jade +++ b/app/templates/play/level/tome/cast_button.jade @@ -1,7 +1,10 @@ button.btn.btn-lg.btn-illustrated.cast-button(title=castVerbose) span(data-i18n="play_level.tome_run_button_ran") Ran -button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose, data-i18n="play_level.tome_submit_button") Submit - -button.btn.btn-lg.btn-illustrated.done-button.secret - span(data-i18n="play_level.done") Done +if !observing + button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose) + span(data-i18n="play_level.tome_submit_button") Submit + span.spl.secret.submit-again-time + + button.btn.btn-lg.btn-illustrated.done-button.secret + span(data-i18n="play_level.done") Done diff --git a/app/templates/play/level/tome/problem_alert.jade b/app/templates/play/level/tome/problem_alert.jade index 6951145b8..a227658c3 100644 --- a/app/templates/play/level/tome/problem_alert.jade +++ b/app/templates/play/level/tome/problem_alert.jade @@ -6,3 +6,4 @@ if hint span.problem-subtitle!= message else span.problem-title!= message +button.btn.btn-lg.btn-info.banner#problem-alert-help-button(data-i18n="play_level.problem_alert_help") diff --git a/app/templates/play/level/tome/spell_palette.jade b/app/templates/play/level/tome/spell_palette.jade index fd8ca6423..d56b1d444 100644 --- a/app/templates/play/level/tome/spell_palette.jade +++ b/app/templates/play/level/tome/spell_palette.jade @@ -1,4 +1,3 @@ -img(src="/images/level/code_palette_wood_background.png", draggable="false").code-palette-background span.code-palette-background if entryGroupSlugs // Non-hero; group by entry groups, or maybe nothing. @@ -11,9 +10,29 @@ if entryGroupSlugs each slug, group in entryGroupSlugs div(id="palette-tab-" + slug, class="tab-pane nano" + (group == "this" || slug == defaultGroupSlug ? " active" : "")) div(class="properties properties-" + slug + " nano-content") + +else if tabs + // Hero; group by items, but also include tabs + ul(class="nav nav-pills multiple-tabs") + li.active + a(data-toggle="pill", data-target="#palette-tab-this") + h4= thisName + each entries, tab in tabs + li + a(data-toggle="pill", data-target='#palette-tab-' + _.string.slugify(tab)) + h4= tab + .tab-content + div#palette-tab-this.tab-pane.active + .properties.properties-this + each entries, tab in tabs + div(id="palette-tab-" + _.string.slugify(tab), class="tab-pane") + div(class="properties properties-" + _.string.slugify(tab)) + else // Hero; group by items, no tabs. - //h4(data-i18n="play_level.tome_your_skills") - h4(data-i18n="play_level.tome_help") - .properties + if showsHelp + button.btn.btn-sm.btn-info.banner#spell-palette-help-button(data-i18n="play_level.tome_help") + .properties.properties-this + else + .properties.properties-this.no-help diff --git a/app/templates/play/level/tome/spell_palette_entry_popover.jade b/app/templates/play/level/tome/spell_palette_entry_popover.jade index 1271847b2..e75f6451f 100644 --- a/app/templates/play/level/tome/spell_palette_entry_popover.jade +++ b/app/templates/play/level/tome/spell_palette_entry_popover.jade @@ -73,17 +73,17 @@ if !selectedMethod .docs-ace-container .docs-ace if language == 'javascript' - span= doc.owner + '.' + doc.name + '(' + argumentExamples.join(', ') + ');' + span= doc.owner + '.' + docName + '(' + argumentExamples.join(', ') + ');' else if language == 'coffeescript' - span= doc.ownerName + (doc.ownerName == '@' ? '' : '.') + doc.name + ' ' + argumentExamples.join(', ') + span= doc.ownerName + (doc.ownerName == '@' ? '' : '.') + docName + ' ' + argumentExamples.join(', ') else if language == 'python' - span= doc.ownerName + '.' + doc.name + '(' + argumentExamples.join(', ') + ')' + span= doc.ownerName + '.' + docName + '(' + argumentExamples.join(', ') + ')' else if language == 'clojure' - span= '(.' + doc.name + ' ' + doc.ownerName + ' ' + argumentExamples.join(', ') + ')' + span= '(.' + docName + ' ' + doc.ownerName + ' ' + argumentExamples.join(', ') + ')' else if language == 'lua' - span= doc.ownerName + ':' + doc.name + '(' + argumentExamples.join(', ') + ')' + span= doc.ownerName + ':' + docName + '(' + argumentExamples.join(', ') + ')' else if language == 'io' - span= (doc.ownerName == 'this' ? '' : doc.ownerName + ' ') + doc.name + '(' + argumentExamples.join(', ') + ')' + span= (doc.ownerName == 'this' ? '' : doc.ownerName + ' ') + docName + '(' + argumentExamples.join(', ') + ')' if (doc.type != 'function' && doc.type != 'snippet') || doc.name == 'now' p.value diff --git a/app/templates/main-play-view.jade b/app/templates/play/main-play-view.jade similarity index 100% rename from app/templates/main-play-view.jade rename to app/templates/play/main-play-view.jade diff --git a/app/templates/game-menu/game-menu-modal.jade b/app/templates/play/menu/game-menu-modal.jade similarity index 84% rename from app/templates/game-menu/game-menu-modal.jade rename to app/templates/play/menu/game-menu-modal.jade index 689c41b53..ed7beae05 100644 --- a/app/templates/game-menu/game-menu-modal.jade +++ b/app/templates/play/menu/game-menu-modal.jade @@ -6,10 +6,11 @@ span.glyphicon.glyphicon-remove ul#game-menu-nav.nav.nav-pills.nav-stacked - li - a#change-hero-tab - span.glyphicon.glyphicon-user - span(data-i18n='[title]game_menu.choose_hero_caption;play.change_hero') + if showsChooseHero + li + a#change-hero-tab + span.glyphicon.glyphicon-user + span(data-i18n='[title]game_menu.choose_hero_caption;play.change_hero') for submenu, index in submenus li(class=submenu === showTab ? "active" : "") diff --git a/app/templates/play/menu/guide-view.jade b/app/templates/play/menu/guide-view.jade new file mode 100644 index 000000000..a30740bb2 --- /dev/null +++ b/app/templates/play/menu/guide-view.jade @@ -0,0 +1,20 @@ +if docs.length === 1 + if showVideo + h3(id='help-video-heading', data-i18n="game_menu.guide_video_tutorial") + if videoLocked + p(data-i18n="subscribe.unlock_help_videos") Subscribe to unlock all video tutorials. + button.start-subscription-button.btn.btn-lg.btn-success(data-i18n="subscribe.subscribe_title") Subscribe + else + div(id="help-video-player") + h3(data-i18n="game_menu.guide_tips") + div + != docs[0].html + +else + ul.nav.nav-tabs + for doc in docs + li + a(data-target="#docs_tab_#{doc.slug}", data-toggle="tab") #{doc.name} + div.tab-content + for doc in docs + div.tab-pane(id="docs_tab_#{doc.slug}")!= doc.html diff --git a/app/templates/game-menu/inventory-modal.jade b/app/templates/play/menu/inventory-modal.jade similarity index 69% rename from app/templates/game-menu/inventory-modal.jade rename to app/templates/play/menu/inventory-modal.jade index f18aeb976..1d8171a12 100644 --- a/app/templates/game-menu/inventory-modal.jade +++ b/app/templates/play/menu/inventory-modal.jade @@ -2,6 +2,8 @@ .modal-content img(src="/images/pages/play/modal/inventory-background.png", draggable="false")#play-items-modal-narrow-bg + h1(data-i18n="game_menu.inventory_tab") + div#gems-count-container span#gems-count.big-font= gems @@ -26,12 +28,21 @@ .nano .nano-content if itemGroups + if itemGroups.requiredPurchaseItems.models.length + h4#required-purchase-description(data-i18n="inventory.required_purchase_title") + for item in itemGroups.requiredPurchaseItems.models + if selectedHeroClass && item.classes.indexOf(selectedHeroClass) > -1 + .item(class=item.classes, data-item-id=item.id) + img(draggable="false") + .clearfix + if itemGroups.availableItems.models.length h4#available-description(data-i18n="inventory.available_item") for item in itemGroups.availableItems.models - .item.available(class=item.classes, data-item-id=item.id) - img(src=item.getPortraitURL()) - button.btn.equip-item(data-i18n="inventory.equip") + if selectedHeroClass && item.classes.indexOf(selectedHeroClass) > -1 + .item.available(class=item.classes, data-item-id=item.id) + img + button.btn.equip-item(data-i18n="inventory.equip") .clearfix #double-click-hint.alert.alert-info @@ -41,19 +52,17 @@ if itemGroups.restrictedItems.models.length h4#restricted-description(data-i18n="inventory.restricted_title") for item in itemGroups.restrictedItems.models - .item(class=item.classes, data-item-id=item.id) - img(src=item.getPortraitURL(), draggable="false") + if selectedHeroClass && item.classes.indexOf(selectedHeroClass) > -1 + .item(class=item.classes, data-item-id=item.id) + img(draggable="false") .clearfix if itemGroups.lockedItems.models.length h4#locked-description(data-i18n="play.locked") for item in itemGroups.lockedItems.models - .item(class=item.classes, data-item-id=item.id) - img(src=item.getPortraitURL(), draggable="false") - if item.level - .required-level - span(data-i18n="general.player_level") - span.spl= item.level + if selectedHeroClass && item.classes.indexOf(selectedHeroClass) > -1 + .item(class=item.classes, data-item-id=item.id) + img(draggable="false") .clearfix #item-details-view diff --git a/app/templates/game-menu/item-view.jade b/app/templates/play/menu/item-view.jade similarity index 100% rename from app/templates/game-menu/item-view.jade rename to app/templates/play/menu/item-view.jade diff --git a/app/templates/game-menu/multiplayer-view.jade b/app/templates/play/menu/multiplayer-view.jade similarity index 100% rename from app/templates/game-menu/multiplayer-view.jade rename to app/templates/play/menu/multiplayer-view.jade diff --git a/app/templates/game-menu/options-view.jade b/app/templates/play/menu/options-view.jade similarity index 83% rename from app/templates/game-menu/options-view.jade rename to app/templates/play/menu/options-view.jade index 5f43a07a8..451d960df 100644 --- a/app/templates/game-menu/options-view.jade +++ b/app/templates/play/menu/options-view.jade @@ -22,16 +22,6 @@ span(data-i18n="options.music_label") Music span.help-block(data-i18n="options.music_description") Turn background music on/off. - .form-group.select-group - label.control-label(for="option-autorun-delay", data-i18n="options.autorun_label") Autorun - select#option-autorun-delay.form-control(name="autorunDelay") - option(value=1000, selected=(autorunDelay === 1000), data-i18n="common.delay_1_sec") 1 second - option(value=3000, selected=(autorunDelay === 3000), data-i18n="common.delay_3_sec") 3 seconds - option(value=5000, selected=(autorunDelay === 5000), data-i18n="common.delay_5_sec") 5 seconds - option(value=90019001, selected=(autorunDelay === 90019001), data-i18n="common.manual") Manual - span.help-block(data-i18n="options.autorun_description") Control automatic code execution. - - img.hr(src="/images/pages/play/modal/hr.png", draggable="false") h3(data-i18n="options.editor_config_title") Editor Configuration diff --git a/app/templates/game-menu/save-load-view.jade b/app/templates/play/menu/save-load-view.jade similarity index 100% rename from app/templates/game-menu/save-load-view.jade rename to app/templates/play/menu/save-load-view.jade diff --git a/app/templates/play/modal/buy-gems-modal.jade b/app/templates/play/modal/buy-gems-modal.jade index 6884890eb..340d21cb8 100644 --- a/app/templates/play/modal/buy-gems-modal.jade +++ b/app/templates/play/modal/buy-gems-modal.jade @@ -1,5 +1,6 @@ .modal-dialog .modal-content + if state === 'purchasing' .alert.alert-info(data-i18n="buy_gems.purchasing") @@ -8,7 +9,8 @@ else img(src="/images/pages/play/modal/buy-gems-background.png")#buy-gems-background - + h1(data-i18n="play.buy_gems") + #products for product in products .product @@ -16,6 +18,16 @@ h3(data-i18n=product.i18n) button.btn.btn-illustrated.btn-lg(value=product.id) span= product.price + + .product + h4(data-i18n="buy_gems.price") x3500 / mo + h3(data-i18n="account.subscription") + if me.isPremium() + button.disabled.start-subscription-button.btn.btn-lg.btn-illustrated.btn-success + | ✓ + span(data-i18n="account.subscribed") + else + button.start-subscription-button.btn.btn-lg.btn-illustrated.btn-success(data-i18n="subscribe.subscribe_title") Subscribe if state === 'declined' #declined-alert.alert.alert-danger.alert-dismissible @@ -25,6 +37,16 @@ if state === 'unknown_error' #error-alert.alert.alert-danger.alert-dismissible - span(data-i18n="loading_error.unknown") button.close(type="button" data-dismiss="alert") - span(aria-hidden="true") × \ No newline at end of file + span(aria-hidden="true") × + p(data-i18n="loading_error.unknown") + p= stateMessage + + if state === 'recovered_charge' + #recovered-alert.alert.alert-danger.alert-dismissible + span(data-i18n="buy_gems.recovered") + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + + div#close-modal + span.glyphicon.glyphicon-remove diff --git a/app/templates/play/modal/item-details-view.jade b/app/templates/play/modal/item-details-view.jade index 7039e01e7..58eff3851 100644 --- a/app/templates/play/modal/item-details-view.jade +++ b/app/templates/play/modal/item-details-view.jade @@ -13,7 +13,7 @@ img.hr(src="/images/pages/play/modal/hr.png", draggable="false") for stat in stats - div.stat-row.big-font + div(class="stat-row big-font" + (/^en/.test(me.get('preferredLanguage')) && stat.matchedShortName ? " short-name" : "")) div.stat-label= stat.name div.stat= stat.display img.hr(src="/images/pages/play/modal/hr.png", class=stat.isLast ? "" : "faded", draggable="false") @@ -37,7 +37,6 @@ if item && !item.owned if item.unequippable - // Temp, while we only have Warriors: prevent them from buying non-Warrior stuff button.btn.big-font.disabled.unequippable #{item.get('heroClass')} Only else button#selected-item-unlock-button.btn.big-font.unlock-button(data-item-id=item.id) diff --git a/app/templates/play/modal/leaderboard-modal.jade b/app/templates/play/modal/leaderboard-modal.jade new file mode 100644 index 000000000..a2e4a417d --- /dev/null +++ b/app/templates/play/modal/leaderboard-modal.jade @@ -0,0 +1,27 @@ +.modal-dialog + .modal-content + img(src="/images/pages/play/modal/leaderboard-background.png", draggable="false")#leaderboard-background + + h1.level-title= levelName + + div#close-modal + span.glyphicon.glyphicon-remove + + ul#leaderboard-nav.nav.nav-pills.nav-stacked + - var lastScoreType = null; + for submenu, index in submenus + if lastScoreType && submenu.scoreType != lastScoreType + br + li(class=index ? "" : "active") + a(href='#' + submenu.scoreType + '-' + submenu.timespan + '-view', data-toggle='tab') + if submenu.scoreType != lastScoreType + .scoreType(data-i18n='leaderboard.' + submenu.scoreType.replace('-', '_'))= submenu.scoreType + else + .scoreType + .timespan(data-i18n='leaderboard.' + submenu.timespan) + - lastScoreType = submenu.scoreType; + + .tab-content.leaderboard-tab-content + for submenu, index in submenus + .tab-pane(id=submenu.scoreType + '-' + submenu.timespan + '-view') + .leaderboard-tab-view \ No newline at end of file diff --git a/app/templates/play/modal/leaderboard-tab-view.jade b/app/templates/play/modal/leaderboard-tab-view.jade new file mode 100644 index 000000000..54ae254b0 --- /dev/null +++ b/app/templates/play/modal/leaderboard-tab-view.jade @@ -0,0 +1,37 @@ +h1 + span.spr(data-i18n="leaderboard.top_players") Top Players by + span(data-i18n="leaderboard.#{scoreType.replace('-', '_')}") + span.spr , + span(data-i18n="leaderboard.#{timespan}") + +if topScores + table.table.table-bordered.table-condensed.table-hover + thead + tr + th(colspan=4, data-i18n="general.player") + th(data-i18n="general.score") + th(data-i18n="general.when") + th + tbody + for row, rank in topScores + - var isMyRow = row.creator == me.id + - var viewable = rank >= 5 || me.isAdmin(); + tr(class=isMyRow ? "success" : "" + (viewable ? " viewable" : ""), data-player-id=row.creator, data-session-id=row.session, title=viewable ? "View solution" : "Can't view top 5 solutions") + td.rank-cell= rank + 1 + td.code-language-cell(style="background-image: url(/images/common/code_languages/#{row.codeLanguage}_small.png)" title=_.string.capitalize(row.codeLanguage)) + td.hero-portrait-cell(style="background-image: url(/file/db/thang.type/#{row.hero}/portrait.png)") + td.name-col-cell= row.creatorName || "Anonymous" + td.score-cell= row.score + td.ago-cell= row.ago + td.viewable-cell + if viewable + if (me.get('preferredLanguage', true) || 'en-US').substr(0, 2) == 'en' + .btn.btn-xs.btn-info Watch + else + .glyphicon.glyphicon-eye-open + else + .glyphicon.glyphicon-eye-close +else if loading + h3(data-i18n="common.loading") +else + h3 No scores yet. diff --git a/app/templates/play/modal/play-account-modal.jade b/app/templates/play/modal/play-account-modal.jade index 1c40e212e..a10afa8e8 100644 --- a/app/templates/play/modal/play-account-modal.jade +++ b/app/templates/play/modal/play-account-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="play.account") Account @@ -7,4 +7,4 @@ block modal-body-content #account-settings-view block modal-footer-content - #save-button.btn-lg.btn.disabled(data-i18n="general.save" disabled="true") No Changes \ No newline at end of file + #save-button.btn-lg.btn.disabled(data-i18n="delta.no_changes" disabled="true") No Changes diff --git a/app/templates/play/modal/play-achievements-modal.jade b/app/templates/play/modal/play-achievements-modal.jade index 71e593170..bf90d77e9 100644 --- a/app/templates/play/modal/play-achievements-modal.jade +++ b/app/templates/play/modal/play-achievements-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="play.achievements") Achievements @@ -8,7 +8,7 @@ block modal-body-content .panel(class=achievement.earned ? 'earned' : '') .panel-body img.icon(src=achievement.getImageURL(), draggable="false") - h3= achievement.name + h3= achievement.name + (achievement.earned && achievement.earned.get('achievedAmount') ? (' - ' + achievement.earned.get('achievedAmount') + 'x') : '') p= achievement.description if achievement.earnedDate @@ -17,16 +17,15 @@ block modal-body-content .created(data-i18n="user.status_unfinished") .rewards - - rewards = achievement.get('rewards') - - rewards = { gems: 100 } + - rewards = achievement.get('rewards'); if rewards && rewards.gems span.gems.label.label-default - span= rewards.gems + span= achievement.earnedGems || rewards.gems img.gem(src="/images/common/gem.png", draggable="false") - - worth = achievement.get('worth') + - worth = achievement.get('worth'); if worth span.worth.label.label-default - span #{worth}xp + span #{achievement.earnedPoints || worth}xp // maybe add more icons/numbers for items, heroes, levels, xp? block modal-footer \ No newline at end of file diff --git a/app/templates/play/modal/play-heroes-modal.jade b/app/templates/play/modal/play-heroes-modal.jade index 132868074..8e8c3f122 100644 --- a/app/templates/play/modal/play-heroes-modal.jade +++ b/app/templates/play/modal/play-heroes-modal.jade @@ -5,6 +5,11 @@ h1(data-i18n="choose_hero.choose_hero") + div#gems-count-container + span#gems-count + .gem.gem-20 + span.spl= gems + div#close-modal span.glyphicon.glyphicon-remove @@ -15,7 +20,10 @@ li(data-hero-id=hero.get('original'), title=hero.name, data-slide-to=index, data-target="#hero-carousel", class="hero-indicator hero-index-" + index + (hero.locked ? " locked" : "") + (hero.purchasable ? " purchasable" : "") + (hero.restricted ? " restricted" : "")) .hero-avatar if hero.locked && !hero.purchasable - img.lock-indicator(src="/images/pages/game-menu/lock.png", draggable="false") + if isIE + img.lock-indicator(src="/images/pages/game-menu/lock-processed.png", draggable="false") + else + img.lock-indicator(src="/images/pages/game-menu/lock.png", draggable="false") .carousel-inner for hero in heroes div(class="item hero-item" + (hero.locked ? " locked" : "") + (hero.purchasable ? " purchasable" : "") + (hero.restricted ? " restricted" : ""), data-hero-id=hero.get('original')) @@ -29,7 +37,11 @@ .hero-stat-row .stat-label(data-i18n='choose_hero.status') .stat-value.hero-status-value(data-i18n=hero.restricted ? 'inventory.restricted_title' : (hero.purchasable ? 'play.purchasable' : (hero.locked ? 'play.locked' : 'play.available'))) - + + .hero-stat-row + .stat-label(data-i18n='choose_hero.hero_type') + .stat-value(data-i18n='general.' +hero.class) + .hero-stat-row .stat-label(data-i18n='choose_hero.weapons') .stat-value(data-i18n='choose_hero.weapons_'+hero.class) @@ -40,11 +52,11 @@ .stat-label(data-i18n='choose_hero.skills') .stat-value= hero.stats.skills.join(', ') for stat in ['attack', 'health', 'speed'] - .hero-stat-row(class=stat) + .hero-stat-row(class=stat, title=hero.stats[stat].description) .stat-label(data-i18n='choose_hero.'+stat) .stat-value .stat-progress - .stat-progress-bar(style="width: " + (parseInt(hero.stats[stat]*100)) + "%") + .stat-progress-bar(style="width: " + (parseInt(hero.stats[stat].relative * 100)) + "%") a.left(role="button", data-slide="prev", href="#hero-carousel") span.glyphicon.glyphicon-play @@ -53,18 +65,27 @@ #hero-footer if visibleHero - if visibleHero.restricted + if !visibleHero.get('original') + #loading-hero-explanation + h2(data-i18n="common.loading") Loading... + + else if visibleHero.restricted #restricted-hero-explanation h2 span= visibleHero.name span.spl(data-i18n="inventory.restricted_title") Restricted span.spr(data-i18n="choose_hero.restricted_to_certain_heroes") Only certain heroes can play this level. + button.btn.disabled.btn-illustrated#restricted-hero-button(data-i18n="inventory.restricted_title") Restricted else if visibleHero.purchasable #purchasable-hero-explanation h2(data-i18n="choose_hero.available_for_purchase") Available for Purchase button.btn.unlock-button#purchase-hero-button span.spr(data-i18n="play.unlock") Unlock + - if(!visibleHero.get('gems')) { + - console.error('Huh, we loaded the hero with no gem cost.'); + - visibleHero.set('gems', {ninja: 400, librarian: 630, samurai: 1000, trapper: 1400, "potion-master": 1800, "forest-archer": 2500, sorcerer: 3400, raiser: 4600, necromancer: 6300, pixie: 8500, goliath: 12000, guardian: 16000, "dark-wizard": 21000, assassin: 29000}[visibleHero.get('slug')] || 99999); + - } span= visibleHero.get('gems') span.gem.gem-20 diff --git a/app/templates/play/modal/play-items-modal.jade b/app/templates/play/modal/play-items-modal.jade index 05656e783..00208f293 100644 --- a/app/templates/play/modal/play-items-modal.jade +++ b/app/templates/play/modal/play-items-modal.jade @@ -13,12 +13,25 @@ ul.nav.nav-pills.nav-stacked for category, index in itemCategories - li(class=index ? "" : "active") + li(class=index ? "" : "active", id=category + '-tab') a.one-line(href="#item-category-" + category, data-toggle="tab") img.tab-icon(src="/images/pages/play/modal/item-icon-"+category+".png", draggable="false") span.big-font= itemCategoryNames[index] - - + + #hero-type-select.btn-group(data-toggle="buttons") + label.btn.active#all + input(type="radio", name="hero-class-select", autocomplete="off") + span(data-i18n="editor.level_tab_thangs_all") + label.btn#warrior + input(type="radio", name="hero-class-select", autocomplete="off") + span(data-i18n="general.warrior") + label.btn#ranger + input(type="radio", name="hero-class-select", autocomplete="off") + span(data-i18n="general.ranger") + label.btn#wizard + input(type="radio", name="hero-class-select", autocomplete="off") + span(data-i18n="general.wizard") + .tab-content for category, index in itemCategories .tab-pane(id="item-category-" + category, class=index ? "" : "active") @@ -27,19 +40,18 @@ for item in itemCategoryCollections[category].models - var hidden = item.comingSoon && !me.isAdmin() - hidden = hidden || (!item.get('gems') && !item.owned) - div(class="item" + (hidden ? " hide" : "") + (item.silhouetted && !item.owned ? " silhouetted" : ""), data-item-id=item.id) + div(class="item " + item.get('heroClass') + (hidden ? " hide" : "") + (item.silhouetted && !item.owned ? " silhouetted" : ""), data-item-id=item.id) if item.silhouetted && !item.owned span.glyphicon.glyphicon-lock.bolder span.glyphicon.glyphicon-lock - img.item-silhouette(src=item.getPortraitURL(), draggable="false") + img.item-silhouette(draggable="false") if item.level .required-level div(data-i18n="general.player_level") div= item.level else strong.big-font= item.name - img.item-img(src=item.getPortraitURL(), draggable="false") - img.item-shadow(src=item.getPortraitURL(), draggable="false") + img.item-img(draggable="false") if item.owned span.big-font.owned(data-i18n="play.owned") @@ -50,7 +62,6 @@ img(src="/images/common/gem.png", draggable="false") span.big-font= item.get('gems') if item.unequippable - // Temp, while we only have Warriors: prevent them from buying non-Warrior stuff span.big-font.unequippable= item.get('heroClass') else button.btn.unlock-button.big-font(data-i18n="play.unlock", data-item-id=item.id) diff --git a/app/templates/play/modal/play-level-modal.jade b/app/templates/play/modal/play-level-modal.jade index dbcce9e80..22a19a8dd 100644 --- a/app/templates/play/modal/play-level-modal.jade +++ b/app/templates/play/modal/play-level-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h1#choose-hero-header.choose-hero-active.secret(data-i18n="choose_hero.choose_hero") Choose Your Hero diff --git a/app/templates/play/modal/play-settings-modal.jade b/app/templates/play/modal/play-settings-modal.jade index ee37a395d..7f2d4460d 100644 --- a/app/templates/play/modal/play-settings-modal.jade +++ b/app/templates/play/modal/play-settings-modal.jade @@ -1,4 +1,4 @@ -extends /templates/modal/modal_base +extends /templates/core/modal-base block modal-header-content h3(data-i18n="play.settings") Settings diff --git a/app/templates/play/modal/poll-modal.jade b/app/templates/play/modal/poll-modal.jade new file mode 100644 index 000000000..69b2bfbfd --- /dev/null +++ b/app/templates/play/modal/poll-modal.jade @@ -0,0 +1,41 @@ +extends /templates/core/modal-base +block modal-header-content + h1 + span= i18n(poll.attributes, "name") + +block modal-body-content + .description + if poll.get("description") + div!= marked(i18n(poll.attributes, "description")) + else + div + + .answers-container-wrapper + .answers-container + table.table.table-hover + for answer in poll.get("answers") || [] + tr(class="answer", data-answer=answer.key) + td!= marked(i18n(answer, "text")) + td.graph-cell + .progress + .progress-bar + td.votes-cell + span.badge.vote-percentage + if me.isAdmin() + td.votes-cell + span.badge.vote-count + + .random-gems-container-wrapper + .random-gems-container + code.random-gems-code + span randomNumber = Math.random() + span.comment#random-number-comment + code.random-gems-code + span gems = Math.ceil(2 * randomNumber * me.level) + span.comment#random-gems-comment + code.random-gems-code + span me.gems += gems + span.comment#total-gems-comment + +block modal-footer-content + button.btn.btn-illustrated.btn-lg.done-button(data-dismiss="modal", aria-hidden="true", data-i18n="play_level.done") Done diff --git a/app/templates/play/modal/share-progress-modal.jade b/app/templates/play/modal/share-progress-modal.jade new file mode 100644 index 000000000..c3feaf5a0 --- /dev/null +++ b/app/templates/play/modal/share-progress-modal.jade @@ -0,0 +1,24 @@ +.modal-dialog + .modal-content + img.background-img(src="/images/pages/play/modal/parental_prompt_modal_background.png") + img.wizard-img(src="/images/pages/play/modal/parental_nudge_wizard.png") + + .blurb-container + h1(data-i18n="share_progress_modal.title") + p(data-i18n="share_progress_modal.blurb") + + .container-fluid.send-container + .row + .col-xs-12.email-form + p(data-i18n="share_progress_modal.form_blurb") + div + label(data-i18n="share_progress_modal.form_label") + input.form-control.email-input(type='email' data-i18n="[placeholder]share_progress_modal.placeholder") + .row + .col-xs-8 + .email-invalid(data-i18n="share_progress_modal.email_invalid") + .col-xs-4.text-right + button.btn.btn-illustrated.send-btn(data-i18n="common.send") + .row.continue-container + .col-xs-12.text-left + a.continue-link(data-i18n="common.continue") diff --git a/app/templates/play/spectate.jade b/app/templates/play/spectate.jade index a8b6e8cf9..432c8ad4d 100644 --- a/app/templates/play/spectate.jade +++ b/app/templates/play/spectate.jade @@ -11,7 +11,3 @@ #level-chat-view #playback-view #thang-hud -.footer - .content - p(class='footer-link-text') - a(title='Send CodeCombat a message', tabindex=-1, data-toggle="coco-modal", data-target="core/ContactModal", data-i18n="nav.contact") Contact \ No newline at end of file diff --git a/app/templates/play/world-map-view.jade b/app/templates/play/world-map-view.jade deleted file mode 100644 index a11003fff..000000000 --- a/app/templates/play/world-map-view.jade +++ /dev/null @@ -1,74 +0,0 @@ -.map - .gradient.horizontal-gradient.top-gradient - .gradient.vertical-gradient.right-gradient - .gradient.horizontal-gradient.bottom-gradient - .gradient.vertical-gradient.left-gradient - img.map-background(src="/images/pages/play/map_" + mapType + ".jpg", alt="", draggable="false") - - - var seenNext = nextLevel; - each level in campaign.levels - if !level.hidden - - var next = level.id == nextLevel || (!seenNext && levelStatusMap[level.id] != "complete" && !level.locked && !level.disabled && (!level.practice || me.getBranchingGroup() == 'all-practice')); - - seenNext = seenNext || next; - div(style="left: #{level.x}%; bottom: #{level.y}%; background-color: #{level.color}", class="level" + (next ? " next" : "") + (level.disabled ? " disabled" : "") + (level.locked ? " locked" : "") + " " + levelStatusMap[level.id] || "", data-level-id=level.id, title=level.name + (level.disabled ? ' (Coming Soon to Adventurers)' : '')) - a(href=level.type == 'hero' ? '#' : level.disabled ? "/play" : "/play/#{level.levelPath || 'level'}/#{level.id}", disabled=level.disabled, data-level-id=level.id, data-level-path=level.levelPath || 'level', data-level-name=level.name) - div(style="left: #{level.x}%; bottom: #{level.y}%", class="level-shadow" + (next ? " next" : "") + " " + levelStatusMap[level.id] || "") - .level-info-container(data-level-id=level.id, data-level-path=level.levelPath || 'level', data-level-name=level.name) - div(class="level-info " + (levelStatusMap[level.id] || "")) - h3= level.name + (level.disabled ? " (Coming soon!)" : (level.locked ? " (Locked)" : "")) - .level-description= level.description - if level.disabled - p - span.spr(data-i18n="play.awaiting_levels_adventurer_prefix") We release five levels per week. - a.spr(href="/contribute/adventurer") - strong(data-i18n="play.awaiting_levels_adventurer") Sign up as an Adventurer - span.spl(data-i18n="play.awaiting_levels_adventurer_suffix") to be the first to play new levels. - - - var playCount = levelPlayCountMap[level.id] - if playCount && playCount.sessions > 20 - div - span.spr #{playCount.sessions} - span(data-i18n="play.players") players - span.spr , #{Math.round(playCount.playtime / 3600)} - span(data-i18n="play.hours_played") hours played - .campaign-label(style="color: #{campaign.color}")= campaign.name - if isIPadApp && !level.disabled && !level.locked - button.btn.btn-success.btn-lg.start-level(data-i18n="common.play") Play - if mapType === 'dungeon' && forestIsAvailable - a#forest-link.glyphicon.glyphicon-share-alt.campaign-switch(href="/play/forest", data-i18n="[title]play.campaign_forest") - if mapType === 'forest' - a#dungeon-link.glyphicon.glyphicon-share-alt.campaign-switch(href="/play/dungeon", data-i18n="[title]play.campaign_dungeon") - -.game-controls.header-font - button.btn.items(data-toggle='coco-modal', data-target='play/modal/PlayItemsModal', data-i18n="[title]play.items") - button.btn.heroes(data-toggle='coco-modal', data-target='play/modal/PlayHeroesModal', data-i18n="[title]play.heroes") - button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements") - if me.get('anonymous') === false || me.get('iosIdentifierForVendor') || isIPadApp - button.btn.gems(data-toggle='coco-modal', data-target='play/modal/BuyGemsModal', data-i18n="[title]play.buy_gems") - if me.isAdmin() - button.btn.account(data-toggle='coco-modal', data-target='play/modal/PlayAccountModal', data-i18n="[title]play.account") - button.btn.settings(data-toggle='coco-modal', data-target='play/modal/PlaySettingsModal', data-i18n="[title]play.settings") - else if me.get('anonymous', true) - button.btn.settings(data-toggle='coco-modal', data-target='core/AuthModal', data-i18n="[title]play.settings") - // Don't show these things, they are bad and take us out of the game. Just wait until the new ones work. - //else - // a.btn.achievements(href="/user/#{me.getSlugOrID()}/stats", data-i18n="[title]play.achievements") - // a.btn.account(href="/user/#{me.getSlugOrID()}", data-i18n="[title]play.account") - // a.btn.settings(href='/account', data-i18n="[title]play.settings") - -.user-status.header-font - span.gem.gem-20 - span#gems-count.spr= me.gems() - span.spl.spr(data-i18n="general.player_level") - span.spr= me.level() - if me.get('anonymous') - span.spr(data-i18n="play.anonymous_player") Anonymous Player - button.btn.btn-default.btn-flat.btn-sm(data-toggle='coco-modal', data-target='core/AuthModal', data-i18n="login.log_in") - else - span.spr= me.get('name') - button#logout-button.btn.btn-default.btn-flat.btn-sm(data-i18n="login.log_out") Log Out - -button.btn.btn-lg.btn-inverse#volume-button(title="Adjust volume") - .glyphicon.glyphicon-volume-off - .glyphicon.glyphicon-volume-down - .glyphicon.glyphicon-volume-up diff --git a/app/templates/teachers-free-trial.jade b/app/templates/teachers-free-trial.jade new file mode 100644 index 000000000..4fae305b6 --- /dev/null +++ b/app/templates/teachers-free-trial.jade @@ -0,0 +1,75 @@ +extends /templates/base + +block content + + h2(data-i18n="teachers_survey.title") + + if me.isAnonymous() + p(data-i18n="teachers_survey.must_be_logged") + else if fetchingData + h4(data-i18n="teachers_survey.retrieving") + else if existingRequests.length > 0 + if existingRequests[0].get('status') === 'submitted' + p + span.spr(data-i18n="teachers_survey.being_reviewed_1") + strong(data-i18n="teachers_survey.being_reviewed_2") + else if existingRequests[0].get('status') === 'approved' + p + span.spr(data-i18n="teachers_survey.approved_1") + strong.spr(data-i18n="teachers_survey.approved_2") + span.spr(data-i18n="teachers_survey.approved_3") + strong= existingRequests[0].get('properties').email + else + p + span.spr(data-i18n="teachers_survey.denied_1") + strong(data-i18n="teachers_survey.denied_2") + p + span.spr(data-i18n="teachers_survey.contact_1") + a(href='mailto:team@codecombat.com') team@codecombat.com + span.spl(data-i18n="teachers_survey.contact_2") + else + p + span.spr(data-i18n="teachers_survey.description_1") + a(href='/teachers', data-i18n="teachers_survey.description_2") + span.spl(data-i18n="teachers_survey.description_3") + p(data-i18n="teachers_survey.description_4") + p.container-email-address + label.control-label(data-i18n="teachers_survey.email") + br + input.control-label.input-email-address(type='text', value=email) + p.container-school + label.control-label(data-i18n="teachers_survey.school") + br + input.control-label.input-school(type='text') + p.container-location + label.control-label(data-i18n="teachers_survey.location") + br + input.control-label.input-location(type='text') + p.container-age + label.control-label(data-i18n="teachers_survey.age_students") + div + input(type="radio", name="age", value="Under 14") + span.spl(data-i18n="teachers_survey.under") + span.spl 14 + div + input(type="radio", name="age", value="14-17") + span.spl 14-17 + div + input(type="radio", name="age", value="18+") + span.spl 18+ + div + input.radio-other(type="radio", name="age", value='other') + span.spl.spr(data-i18n="teachers_survey.other") + input.spr.input-age-other(type='text') + p.container-num-students + label.control-label(data-i18n="teachers_survey.amount_students") + br + input.control-label.input-num-students(type='text') + p.container-heard-about + label.control-label(data-i18n="teachers_survey.hear_about") + br + textarea.control-label.input-heard-about(rows=4) + p.error-message(data-i18n="teachers_survey.fill_fields") + p + button.btn.btn-default.submit-button(data-i18n="play_level.tome_submit_button") + p.thanks-submit(data-i18n="teachers_survey.thanks") diff --git a/app/templates/teachers.jade b/app/templates/teachers.jade index 2a5b5b2fb..148c79c74 100644 --- a/app/templates/teachers.jade +++ b/app/templates/teachers.jade @@ -6,59 +6,163 @@ block content .span5 - h2 CodeCombat for Teachers + h2(data-i18n="teachers.title") + p(data-i18n="teachers.intro_1") + p(data-i18n="teachers.intro_2") - h3 Preparation - - p CodeCombat is free to play and does not require students to sign up. We encourage teachers to - a(href="http://codecombat.com/play") play through the campaign - | to try it out, but the only thing you absolutely need to do to be ready is ensure students have access to a computer or iPad. + h3(data-i18n="teachers.free_title") + if me.get('chinaVersion') + p(data-i18n="teachers.cost_china") + else + p(data-i18n="teachers.free_1") + p(data-i18n="teachers.free_2") + h3.teachers-title(data-i18n="teachers.teacher_subs_title") p - | The iPad version is not publicly available yet, but you can - a(href="https://www.youtube.com/watch?v=jgF4YuN7RBk&feature=youtu.be") see a preview video here - | . + span.spr(data-i18n="teachers.teacher_subs_1") + a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2") + span.spl(data-i18n="teachers.teacher_subs_3") - p It is not necessary for teachers to be comfortable with computer science concepts for students to have fun learning with CodeCombat. + h3(data-i18n="teachers.sub_includes_title") + p(data-i18n="teachers.sub_includes_1") + ul + li(data-i18n="teachers.sub_includes_2") + li(data-i18n="teachers.sub_includes_3") + li(data-i18n="teachers.sub_includes_4") + li(data-i18n="teachers.sub_includes_5") + li(data-i18n="teachers.sub_includes_6") + li(data-i18n="teachers.sub_includes_7") - h3 Is it violent? + h3(data-i18n="teachers.who_for_title") + p(data-i18n="teachers.who_for_1") + p(data-i18n="teachers.who_for_2") - p We get this from teachers a lot due to our name. Although CodeCombat does contain cartoon violence, there is nothing graphic in either the visuals or language. If you are comfortable having your students play Angry Birds, you will be comfortable with CodeCombat. + h3(data-i18n="teachers.monitor_progress_title") + p + span.spr(data-i18n="teachers.monitor_progress_1") + a(href='/clans', data-i18n="clans.clan") + span.spl(data-i18n="teachers.monitor_progress_2") + p + span.spr(data-i18n="teachers.monitor_progress_3") + a(href='/clans', data-i18n="clans.clans") + span.spl(data-i18n="teachers.monitor_progress_4") + p(data-i18n="teachers.monitor_progress_5") + h4(data-i18n="teachers.sub_includes_7") + ul + li + strong Track concepts + span.spl learned by each student + li Track levels completed for each student + li + span See your students' + strong.spl solutions + li Sort students by name or progress + li + strong Requires invitation + span.spl to join + p + img(src='/images/pages/clans/dashboard_preview.png' height='400') + p + span.spr(data-i18n="teachers.private_clans_2") + a(href='/clans', data-i18n="clans.clan") + span(data-i18n="teachers.private_clans_3") + p Private clans require a subscription to create or join. - h3 Is it for girls? + h3(data-i18n="teachers.material_title") + if me.get('chinaVersion') + p(data-i18n="teachers.material_china") + else + p(data-i18n="teachers.material_1") - p There are three game modes in CodeCombat: building, puzzles, and combat. We have intentionally designed each to appeal to both boys and girls and think that the building and puzzle levels especially differentiate the game from violent triple A titles that repel female players. + h3(data-i18n="teachers.concepts_title") - h3 What do we cover? + //- TODO: i18n for concepts? - p There are 12 levels in the Hour of Code tutorial that teach and reinforce 5 specific computer science concepts: + table.table.table-condensed.concepts-table + thead + tr + th + a(href='/play/dungeon') Kithgard Dungeon + th + a(href='/play/forest') Backwoods Forest + th + a(href='/play/desert') Sarven Desert + th + a(href='/play/mountain') Cloudrip Mountain + tbody + tr + td Basic Syntax + td If Statements + td Arithmetic + td Object Literals + tr + td Methods + td Relational Operators + td While Loops + td For Loops + tr + td Parameters + td Object Properties + td Break Statements + td Functions + tr + td Strings + td Input Handling + td Arrays + td Drawing + tr + td Loops + td + td String Comparison + td Modulo + tr + td Variables + td + td Finding Min/Max + td - ol - li <strong>Formal notation</strong> - builds an understanding of the importance of syntax in programming. - li <strong>Calling methods</strong> - familiarizes students with the syntax of object-oriented method calls. - li <strong>Parameters</strong> - trains how to pass parameters to functions. - li <strong>Strings</strong> - teaches students about string notation and passing strings as parameters. - li <strong>Loops</strong> - develops the abstraction of designing short programs with loops. + h3(data-i18n="teachers.how_much_title") + p + span(data-i18n="teachers.how_much_1") + span.spr.spl + a(href='/account/subscription', data-i18n="teachers.how_much_2") + span.spr.spl(data-i18n="teachers.how_much_3") + p + span.spr(data-i18n="teachers.how_much_5") + a(href='mailto:team@codecombat.com') team@codecombat.com + span.spl(data-i18n="teachers.how_much_6") + p(data-i18n="teachers.how_much_4") + h4 + a(href='/account/subscription', data-i18n="subscribe.group_discounts") + p(data-i18n="subscribe.group_discounts_1") + table.table.table-condensed.discount-table + tr + td(data-i18n="subscribe.group_discounts_1st") + td(data-i18n="subscribe.group_discounts_full") + tr + td(data-i18n="subscribe.group_discounts_2nd") + td(data-i18n="subscribe.group_discounts_20") + tr + td(data-i18n="subscribe.group_discounts_12th") + td(data-i18n="subscribe.group_discounts_40") - p Students may continue past level 12, depending on their speed and interest, to learn two additional concepts in levels 13-20: + h3(data-i18n="teachers.more_info_title") + p + span.spr(data-i18n="teachers.more_info_1") + a(href='http://discourse.codecombat.com/c/teachers', data-i18n="teachers.more_info_2") + span.spl(data-i18n="teachers.more_info_3") - ol - li <strong>Conditional logic</strong> - when and how to use if/else to control in-game outcomes. - li <strong>Handling player input</strong> - responding to input events to create a user interface. + h3(data-i18n="teachers.sys_requirements_title") + p(data-i18n="teachers.sys_requirements_1") + p(data-i18n="teachers.sys_requirements_2") - h3 System Requirements - - p Because CodeCombat is a game, it is more intensive for computers to run smoothly than video or written tutorials. We are continually improving performance and expect to have the game running smoothly on older machines by December. That said, here are our suggestions for getting the most out of your Hour of Code experience: - - p For mobile players: - - ul - li <strong>Use newer iPads if possible.</strong> Older iPads (iPad 2+) will work, but will play the game more slowly. The app requires iOS 8. - li <strong>Allow players to wear headphones/earbuds to hear the audio.</strong> We help players learn through voiceover and sound effects which will make classrooms noisy and distracting. - - p For desktop and laptop players: - - ul - li <strong>Use newer versions of Chrome or Firefox.</strong> Although CodeCombat will work on browsers as old as IE9, the performance is not as good. - li <strong>Use newer computers.</strong> Older computers, Chromebooks, and netbooks tend to have very few system resources, which makes for a less enjoyable experience. - li <strong>Allow players to wear headphones/earbuds to hear the audio.</strong> We help players learn through voiceover and sound effects which will make classrooms noisy and distracting. \ No newline at end of file + if (me.get('preferredLanguage', true) || 'en-US') == 'pl' + h3 Fundacja IT Leader Club Polska + p + | CodeCombat w marcu 2015 roku nawiązał współpracę z Fundacją IT Leader Club Polska celem promowania nauki programowania wśród najmłodszych w Polsce. Język programowania stał się drugim językiem, bez którego bardzo trudno będzie młodym ludziom w przyszłości zaistnieć na konkurencyjnym rynku pracy. + p + | Poznając podstawy języka programowania z Fundacją IT Leader Club Polska z wykorzystaniem CodeCombat, polskie dzieci będą bardzo szybko potrafiły poznać kluczowe zagadnienia programowania. Poprzez zabawę i interaktywną grę, dzieci szybko przyswoją niezbędną teorię i zaczną czerpać z programowania nie tylko wiedzę ale i pasję na całe życie - czego życzymy wszystkim naszym młodym studentom. W przypadku pytań prosimy o kontakt z Panem Arkadiuszem Lefanowiczem, Przewodniczącym Fundacji IT Leader Club Polska, + a(href="http://www.itleader.org.pl") itleader.org.pl + | , + a(href="mailto:arkadiusz.lefanowicz@itleader.org.pl") arkadiusz.lefanowicz@itleader.org.pl + | . diff --git a/app/templates/test.jade b/app/templates/test-view.jade similarity index 98% rename from app/templates/test.jade rename to app/templates/test-view.jade index 2a46af58a..9851af483 100644 --- a/app/templates/test.jade +++ b/app/templates/test-view.jade @@ -21,4 +21,4 @@ ol.breadcrumb if child.type == 'folder' strong (#{child.size}) -.clearfix \ No newline at end of file +.clearfix diff --git a/app/templates/user/identify-view.jade b/app/templates/user/identify-view.jade new file mode 100644 index 000000000..a5a8db09d --- /dev/null +++ b/app/templates/user/identify-view.jade @@ -0,0 +1,21 @@ +extends /templates/base + +block content + + if callbackURL && callbackSource && callbackID + h3 Share your username? + p + | #{callbackSource} would like to know that you are #{me.get('name') || 'you'} on CodeCombat. + if me.get('anonymous') + br + button.btn.btn-lg.btn-default.header-font.login-button(data-i18n="login.log_in") + else if me.get('name') + a.spl.spr(href="#{callbackURL}") Click here + | to share your username. + else + | But you don't have a username yet. Set one + a.spl(href="/account/settings") here + | . + else + h3 Invalid identify URL. + p callbackID, callbackURL, and callbackSource are needed. diff --git a/app/templates/user/main-user-view.jade b/app/templates/user/main-user-view.jade index 611d212e8..b1bb6ec13 100644 --- a/app/templates/user/main-user-view.jade +++ b/app/templates/user/main-user-view.jade @@ -1,6 +1,14 @@ extends /templates/common/user block append content + ol.breadcrumb + li + a(href="/") + span.glyphicon.glyphicon-home + li + a(href="/account", data-i18n="nav.account") + li.active(data-i18n="nav.profile") + if user .vertical-buffer .row @@ -48,6 +56,33 @@ block append content a(href="/contribute#scribe" data-i18n="classes.scribe_title") Scribe .right-column + + .panel.panel-default + .panel-heading + h3.panel-title(data-i18n="clans.clans") Clans + if (!clans) + .panel-body + p(data-i18n="common.loading") + else if (clans.length) + table.table + tr + th.col-xs-4(data-i18n="clans.name") Name + th.col-xs-4(data-i18n="clans.chieftain") Chieftain + th.col-xs-4(data-i18n="play.heroes") Heroes + each clan in clans + tr + td + a(href="/clans/#{clan.id}")= clan.get('name') + td + if idNameMap && idNameMap[clan.get('ownerID')] + a(href="/user/#{clan.get('ownerID')}")= idNameMap[clan.get('ownerID')] + else + a(href="/user/#{clan.get('ownerID')}") Anoner + td= clan.get('members').length + else + .panel-body + p(data-i18n="user.not_member_of_clans") Not a member of any clans yet. + .panel.panel-default .panel-heading h3.panel-title(data-i18n="user.singleplayer_title") Singleplayer Levels @@ -125,7 +160,7 @@ block append content each achievement, index in earnedAchievements.models tr(class=index > 4 ? 'hide' : '') td= achievement.get('achievementName') - td= moment().format("MMMM Do YYYY", achievement.get('changed')) + td= moment(achievement.get('changed')).format("MMMM Do YYYY") if achievement.get('achievedAmount') td= achievement.get('achievedAmount') else @@ -133,4 +168,3 @@ block append content if earnedAchievements.length > 4 .panel-footer button.btn.btn-info.btn-sm.more-button(data-i18n="editor.more") - diff --git a/app/views/CommunityView.coffee b/app/views/CommunityView.coffee index b551c3b1a..0186ede61 100644 --- a/app/views/CommunityView.coffee +++ b/app/views/CommunityView.coffee @@ -1,5 +1,5 @@ RootView = require 'views/core/RootView' -template = require 'templates/community' +template = require 'templates/community-view' module.exports = class CommunityView extends RootView id: 'community-view' @@ -8,17 +8,12 @@ module.exports = class CommunityView extends RootView afterRender: -> super() @$el.find('.contribute-classes a').each -> - characterClass = $(@).attr('href').split('#')[1] + characterClass = $(@).attr('href').split('/')[2] title = $.i18n.t("classes.#{characterClass}_title") titleDescription = $.i18n.t("classes.#{characterClass}_title_description") - if characterClass is 'artisan' - summary = $.i18n.t("contribute.#{characterClass}_summary_pref") + ' Mondo Bizarro' + $.i18n.t("contribute.#{characterClass}_summary_suf") - else if characterClass is 'scribe' - summary = $.i18n.t("contribute.#{characterClass}_summary_pref") + 'Mozilla Developer Network' + $.i18n.t("contribute.#{characterClass}_summary_suf") - else - summary = $.i18n.t("contribute.#{characterClass}_summary") + summary = $.i18n.t("classes.#{characterClass}_summary") explanation = "<h4>#{title} #{titleDescription}</h4>#{summary}" - $(@).find('img').popover(placement: 'bottom', trigger: 'hover', container: 'body', content: explanation, html: true) + $(@).find('img').popover(placement: 'top', trigger: 'hover', container: 'body', content: explanation, html: true) @$el.find('.logo-row img').each -> - $(@).popover(placement: 'bottom', trigger: 'hover', container: 'body') + $(@).popover(placement: 'top', trigger: 'hover', container: 'body') diff --git a/app/views/DemoView.coffee b/app/views/DemoView.coffee index ea167cd6c..b59ccdcb3 100644 --- a/app/views/DemoView.coffee +++ b/app/views/DemoView.coffee @@ -68,7 +68,7 @@ module.exports = DemoView = class DemoView extends RootView @demoFiles = @getAllDemoFiles() if @subPath prefix = DEMO_REQUIRE_PREFIX + @subPath - @demoFiles = (f for f in @demoFiles when f.startsWith prefix) + @demoFiles = (f for f in @demoFiles when _.string.startsWith f, prefix) runDemo: -> # TODO: Maybe have an option to run all demos in this folder at the same time? diff --git a/app/views/EmployersView.coffee b/app/views/EmployersView.coffee index 86e46fd36..bbb596d3a 100644 --- a/app/views/EmployersView.coffee +++ b/app/views/EmployersView.coffee @@ -27,20 +27,24 @@ module.exports = class EmployersView extends RootView constructor: (options) -> super options + return @candidates = @supermodel.loadCollection(new CandidatesCollection(), 'candidates').model @setFilterDefaults() onLoaded: -> super() + return @setUpScrolling() afterRender: -> super() + return @sortTable() if @candidates.models.length @renderSavedFilters() afterInsert: -> super() + return _.delay @checkForEmployerSignupHash, 500 #fairly hacky, change this in the future @originalBackgroundColor = $('body').css 'background-color' @@ -176,6 +180,7 @@ module.exports = class EmployersView extends RootView getRenderData: -> ctx = super() + return ctx ctx.isEmployer = @isEmployer() #If you change the candidates displayed, change candidatesInFilter() ctx.candidates = _.sortBy @candidates.models, (c) -> -1 * c.get('jobProfile').experience diff --git a/app/views/HomeView.coffee b/app/views/HomeView.coffee index c05a5fbfb..48410c487 100644 --- a/app/views/HomeView.coffee +++ b/app/views/HomeView.coffee @@ -1,5 +1,5 @@ RootView = require 'views/core/RootView' -template = require 'templates/home' +template = require 'templates/home-view' module.exports = class HomeView extends RootView id: 'home-view' @@ -10,7 +10,7 @@ module.exports = class HomeView extends RootView constructor: -> super() - window.tracker?.trackEvent 'Homepage Loaded', category: 'Homepage', ['Google Analytics'] + window.tracker?.trackEvent 'Homepage Loaded', category: 'Homepage' if not me.get('hourOfCode') and @getQueryVariable 'hour_of_code' @setUpHourOfCode() elapsed = (new Date() - new Date(me.get('dateCreated'))) @@ -22,14 +22,16 @@ module.exports = class HomeView extends RootView c = super() if $.browser majorVersion = $.browser.versionNumber - c.isOldBrowser = true if $.browser.mozilla && majorVersion < 21 - c.isOldBrowser = true if $.browser.chrome && majorVersion < 17 - c.isOldBrowser = true if $.browser.safari && majorVersion < 6 + c.isOldBrowser = true if $.browser.mozilla && majorVersion < 25 + c.isOldBrowser = true if $.browser.chrome && majorVersion < 31 # Noticed Gems in the Deep not loading with 30 + c.isOldBrowser = true if $.browser.safari && majorVersion < 6 # 6 might have problems with Aether, or maybe just old minors of 6: https://errorception.com/projects/51a79585ee207206390002a2/errors/547a202e1ead63ba4e4ac9fd else console.warn 'no more jquery browser version...' - c.isEnglish = (me.get('preferredLanguage') or 'en').startsWith 'en' + c.isEnglish = _.string.startsWith (me.get('preferredLanguage') or 'en'), 'en' c.languageName = me.get('preferredLanguage') c.explainsHourOfCode = @explainsHourOfCode + c.isMobile = @isMobile() + c.isIPadBrowser = @isIPadBrowser() c onClickBeginnerCampaign: (e) -> @@ -48,6 +50,6 @@ module.exports = class HomeView extends RootView if elapsed < 5 * 60 * 1000 me.set 'hourOfCode', true me.patch() - # We may also insert the tracking pixel for everyone on the WorldMapView so as to count directly-linked visitors. - $('body').append($('<img src="http://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">')) - application.tracker?.trackEvent 'Hour of Code Begin', {} + # We may also insert the tracking pixel for everyone on the CampaignView so as to count directly-linked visitors. + $('body').append($('<img src="https://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">')) + application.tracker?.trackEvent 'Hour of Code Begin' diff --git a/app/views/core/NotFoundView.coffee b/app/views/NotFoundView.coffee similarity index 75% rename from app/views/core/NotFoundView.coffee rename to app/views/NotFoundView.coffee index 9a43c5399..4db47ebff 100644 --- a/app/views/core/NotFoundView.coffee +++ b/app/views/NotFoundView.coffee @@ -1,5 +1,5 @@ RootView = require 'views/core/RootView' -template = require 'templates/not_found' +template = require 'templates/core/not-found' module.exports = class NotFoundView extends RootView id: 'not-found-view' diff --git a/app/views/TeachersFreeTrialView.coffee b/app/views/TeachersFreeTrialView.coffee new file mode 100644 index 000000000..f26ae8c25 --- /dev/null +++ b/app/views/TeachersFreeTrialView.coffee @@ -0,0 +1,95 @@ +RootView = require 'views/core/RootView' +template = require 'templates/teachers-free-trial' +CocoCollection = require 'collections/CocoCollection' +TrialRequest = require 'models/TrialRequest' + +# TODO: distinguish between this type of existing trial requests and others + +module.exports = class TeachersFreeTrialView extends RootView + id: 'teachers-free-trial-view' + template: template + + events: + 'click .submit-button': 'onClickSubmit' + 'click .input-age-other': 'onClickTextBox' + + constructor: (options) -> + super options + @email = me.get('email') + @refreshData() + + getRenderData: -> + context = super() + context.email = @email + context.existingRequests = @existingRequests.models + context.fetchingData = @fetchingData + context + + refreshData: -> + @fetchingData = true + @existingRequests = new CocoCollection([], { url: '/db/trial.request/-/own', model: TrialRequest, comparator: '_id' }) + @listenToOnce @existingRequests, 'sync', => + @fetchingData = false + @render?() + @supermodel.loadCollection(@existingRequests, 'own_trial_requests', {cache: false}) + + onClickTextBox: (e) -> + $('.radio-other').prop("checked", true) + + onClickSubmit: (e) -> + email = $('.input-email-address').val() + school = $('.input-school').val() + location = $('.input-location').val() + age = $('input[name=age]:checked').val() + age = $('.input-age-other').val() if age is 'other' + numStudents = $('.input-num-students').val() + heardAbout = $('.input-heard-about').val() + + # Validate input + $('.container-email-address').removeClass('has-error') + $('.container-school').removeClass('has-error') + $('.container-location').removeClass('has-error') + $('.container-age').removeClass('has-error') + $('.container-num-students').removeClass('has-error') + $('.container-heard-about').removeClass('has-error') + $('.error-message').hide() + unless email + $('.container-email-address').addClass('has-error') + $('.error-message').show() + return + unless school + $('.container-school').addClass('has-error') + $('.error-message').show() + return + unless location + $('.container-location').addClass('has-error') + $('.error-message').show() + return + unless age + $('.container-age').addClass('has-error') + $('.error-message').show() + return + unless numStudents + $('.container-num-students').addClass('has-error') + $('.error-message').show() + return + unless heardAbout + $('.container-heard-about').addClass('has-error') + $('.error-message').show() + return + + # Save trial request + trialRequest = new TrialRequest + type: 'subscription' + properties: + email: email + school: school + location: location + age: age + numStudents: numStudents + heardAbout: heardAbout + trialRequest.save {}, + error: (model, response, options) => + console.error 'Error saving trial request', response + success: (model, response, options) => + @refreshData() diff --git a/app/views/TestView.coffee b/app/views/TestView.coffee index e49878a92..defeba422 100644 --- a/app/views/TestView.coffee +++ b/app/views/TestView.coffee @@ -1,11 +1,14 @@ -CocoView = require 'views/core/CocoView' -template = require 'templates/test' +RootView = require 'views/core/RootView' +template = require 'templates/test-view' requireUtils = require 'lib/requireUtils' +require 'vendor/jasmine-bundle' +require 'tests' + TEST_REQUIRE_PREFIX = 'test/app/' TEST_URL_PREFIX = '/test/' -module.exports = TestView = class TestView extends CocoView +module.exports = TestView = class TestView extends RootView id: 'test-view' template: template reloadOnClose: true @@ -16,24 +19,8 @@ module.exports = TestView = class TestView extends CocoView constructor: (options, @subPath='') -> super(options) @subPath = @subPath[1..] if @subPath[0] is '/' - @loadTestingLibs() - - loadTestingLibs: -> - @queue = new createjs.LoadQueue() unless @queue - @queue.on('complete', @scriptsLoaded, @) - @queue.on('fileload', @onFileLoad, @) - for f in ['jasmine', 'jasmine-html', 'boot', 'mock-ajax', 'test-app'] - if f not in @loadedFileIDs - @queue.loadFile({ - id: f - src: "/javascripts/#{f}.js" - type: createjs.LoadQueue.JAVASCRIPT - }) - - onFileLoad: (e) -> - @loadedFileIDs.push e.item.id if e.item.id - - scriptsLoaded: -> + + afterInsert: -> @initSpecFiles() @render() TestView.runTests(@specFiles) @@ -55,7 +42,7 @@ module.exports = TestView = class TestView extends CocoView @specFiles = TestView.getAllSpecFiles() if @subPath prefix = TEST_REQUIRE_PREFIX + @subPath - @specFiles = (f for f in @specFiles when f.startsWith prefix) + @specFiles = (f for f in @specFiles when _.string.startsWith f, prefix) @runTests: (specFiles) -> specFiles ?= @getAllSpecFiles() diff --git a/app/views/account/AccountSettingsView.coffee b/app/views/account/AccountSettingsView.coffee index 4d0a8a1cb..6b4cf28b8 100644 --- a/app/views/account/AccountSettingsView.coffee +++ b/app/views/account/AccountSettingsView.coffee @@ -4,6 +4,8 @@ template = require 'templates/account/account-settings-view' forms = require 'core/forms' User = require 'models/User' AuthModal = require 'views/core/AuthModal' +ConfirmModal = require 'views/editor/modal/ConfirmModal' +{logoutUser, me} = require('core/auth') module.exports = class AccountSettingsView extends CocoView id: 'account-settings-view' @@ -16,7 +18,8 @@ module.exports = class AccountSettingsView extends CocoView 'click #toggle-all-button': 'toggleEmailSubscriptions' 'click .profile-photo': 'onEditProfilePhoto' 'click #upload-photo-button': 'onEditProfilePhoto' - + 'click #delete-account-button': 'confirmAccountDeletion' + constructor: (options) -> super options require('core/services/filepicker')() unless window.application.isIPadApp # Initialize if needed @@ -33,16 +36,19 @@ module.exports = class AccountSettingsView extends CocoView c.subs[sub] = 1 for sub in me.getEnabledEmails() c - + #- Form input callbacks - onInputChanged: (e) -> $(e.target).addClass 'changed' - @trigger 'input-changed' + if (JSON.stringify(document.getElementById('email1').className)).indexOf("changed") > -1 or (JSON.stringify(document.getElementById('password1').className)).indexOf("changed") > -1 + $(e.target).removeClass 'changed' + else + @trigger 'input-changed' toggleEmailSubscriptions: => subs = @getSubscriptions() $('#email-panel input[type="checkbox"]', @$el).prop('checked', not _.any(_.values(subs))).addClass('changed') + @trigger 'input-changed' checkNameExists: => name = $('#name', @$el).val() @@ -59,9 +65,82 @@ module.exports = class AccountSettingsView extends CocoView @trigger 'inputChanged', e @$el.find('.gravatar-fallback').toggle not me.get 'photoURL' - + #- Just copied from OptionsView, TODO refactor - + confirmAccountDeletion: -> + forms.clearFormAlerts(@$el) + myEmail = me.get 'email' + email1 = document.getElementById('email1').value + password1 = document.getElementById('password1').value + if Boolean(email1) and email1 is myEmail + isPasswordCorrect = false + toBeDelayed = true + $.ajax + url: '/auth/login' + type: 'POST' + data: + { + username: email1, + password: password1 + } + parse: true + error: (error) -> + toBeDelayed = false + 'Bad Error. Can\'t connect to server or something. ' + error + success: (response, textStatus, jqXHR) -> + toBeDelayed = false + unless jqXHR.status is 200 + return + isPasswordCorrect = true + callback = => + if toBeDelayed + setTimeout callback, 100 + else + if isPasswordCorrect + renderData = + 'confirmTitle': 'Are you really sure?' + 'confirmBody': 'This will completely delete your account. This action CANNOT be undone. Are you entirely sure?' + 'confirmDecline': 'Not really' + 'confirmConfirm': 'Definitely' + confirmModal = new ConfirmModal renderData + confirmModal.on 'confirm', @deleteAccount + @openModalView confirmModal + else + message = $.i18n.t('account_settings.wrong_password', defaultValue: 'Wrong Password.') + err = [message: message, property: 'password1', formatted: true] + forms.applyErrorsToForm(@$el, err) + $('.nano').nanoScroller({scrollTo: @$el.find('.has-error')}) + setTimeout callback, 100 + else + message = $.i18n.t('account_settings.wrong_email', defaultValue: 'Wrong Email.') + err = [message: message, property: 'email1', formatted: true] + forms.applyErrorsToForm(@$el, err) + $('.nano').nanoScroller({scrollTo: @$el.find('.has-error')}) + + + deleteAccount: -> + myID = me.id + $.ajax + type: 'DELETE' + success: -> + noty + timeout: 5000 + text: 'Your account is gone.' + type: 'success' + layout: 'topCenter' + _.delay -> + Backbone.Mediator.publish("auth:logging-out", {}) + window.tracker?.trackEvent 'Log Out', category:'Homepage' if @id is 'home-view' + logoutUser($('#login-email').val()) + , 500 + error: (jqXHR, status, error) -> + console.error jqXHR + timeout: 5000 + text: "Deleting account failed with error code #{jqXHR.status}" + type: 'error' + layout: 'topCenter' + url: "/db/user/#{myID}" + onEditProfilePhoto: (e) -> return if window.application.isIPadApp # TODO: have an iPad-native way of uploading a photo, since we don't want to load FilePicker on iPad (memory) photoContainer = @$el.find('.profile-photo') @@ -69,7 +148,7 @@ module.exports = class AccountSettingsView extends CocoView photoContainer.addClass('saving') onSaved = (uploadingPath) => @$el.find('#photoURL').val(uploadingPath) - @onInputChanged() # cause for some reason editing the value doesn't trigger the jquery event + @$el.find('#photoURL').trigger('change') # cause for some reason editing the value doesn't trigger the jquery event me.set('photoURL', uploadingPath) photoContainer.removeClass('saving').attr('src', me.getPhotoURL(photoContainer.width())) filepicker.pick {mimetypes: 'image/*'}, @onImageChosen(onSaving, onSaved) @@ -88,8 +167,8 @@ module.exports = class AccountSettingsView extends CocoView onImageUploaded: (onSaved, uploadingPath) -> (e) => onSaved uploadingPath - - + + #- Misc getSubscriptions: -> @@ -98,9 +177,9 @@ module.exports = class AccountSettingsView extends CocoView enableds = (i.prop('checked') for i in inputs) _.zipObject emailNames, enableds - + #- Saving changes - + save: -> $('#settings-tabs input').removeClass 'changed' forms.clearFormAlerts(@$el) @@ -158,8 +237,14 @@ module.exports = class AccountSettingsView extends CocoView me.set('photoURL', @$el.find('#photoURL').val()) + permissions = [] + adminCheckbox = @$el.find('#admin') if adminCheckbox.length - permissions = [] permissions.push 'admin' if adminCheckbox.prop('checked') - me.set('permissions', permissions) + + godmodeCheckbox = @$el.find('#godmode') + if godmodeCheckbox.length + permissions.push 'godmode' if godmodeCheckbox.prop('checked') + + me.set('permissions', permissions) diff --git a/app/views/account/InvoicesView.coffee b/app/views/account/InvoicesView.coffee new file mode 100644 index 000000000..cf458485a --- /dev/null +++ b/app/views/account/InvoicesView.coffee @@ -0,0 +1,93 @@ +RootView = require 'views/core/RootView' +template = require 'templates/account/invoices-view' +stripeHandler = require 'core/services/stripe' +utils = require 'core/utils' + +# Internal amount and query params are in cents, display and web form input amount is in USD + +module.exports = class InvoicesView extends RootView + id: "invoices-view" + template: template + + events: + 'click #pay-button': 'onPayButton' + + subscriptions: + 'stripe:received-token': 'onStripeReceivedToken' + + constructor: (options) -> + super(options) + @amount = utils.getQueryVariable('a', 0) + @description = utils.getQueryVariable('d', '') + + getRenderData: -> + c = super() + c.amount = (@amount / 100).toFixed(2) + c.description = @description + c.state = @state + c.stateMessage = @stateMessage + c + + onPayButton: -> + @description = $('#description').val() + + # Validate input + amount = parseFloat($('#amount').val()) + if isNaN(amount) or amount <= 0 + @state = 'validation_error' + @stateMessage = $.t('account_invoices.invalid_amount') + @amount = 0 + @render() + return + + @state = undefined + @stateMessage = undefined + @amount = parseInt(amount * 100) + + # Show Stripe handler + application.tracker?.trackEvent 'Started invoice payment' + @timestampForPurchase = new Date().getTime() + stripeHandler.open + amount: @amount + description: @description + bitcoin: true + alipay: if me.get('chinaVersion') or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto' + + onStripeReceivedToken: (e) -> + data = { + amount: @amount + description: @description + stripe: { + token: e.token.id + timestamp: @timestampForPurchase + } + } + + @state = 'purchasing' + @render() + jqxhr = $.post('/db/payment/custom', data) + + jqxhr.done => + application.tracker?.trackEvent 'Finished invoice payment', + amount: @amount + description: @description + + # Show success UI + @state = 'invoice_paid' + @stateMessage = "$#{(@amount / 100).toFixed(2)} " + $.t('account_invoices.success') + @amount = 0 + @description = '' + @render() + + jqxhr.fail => + if jqxhr.status is 402 + @state = 'declined' + @stateMessage = arguments[2] + else if jqxhr.status is 500 + @state = 'retrying' + f = _.bind @onStripeReceivedToken, @, e + _.delay f, 2000 + else + @state = 'unknown_error' + @stateMessage = "#{jqxhr.status}: #{jqxhr.responseText}" + @render() diff --git a/app/views/account/JobProfileTreemaView.coffee b/app/views/account/JobProfileTreemaView.coffee index 76dc6ae53..3e7d13153 100644 --- a/app/views/account/JobProfileTreemaView.coffee +++ b/app/views/account/JobProfileTreemaView.coffee @@ -1,6 +1,7 @@ CocoView = require 'views/core/CocoView' -template = require 'templates/account/job_profile' +template = require 'templates/account/job-profile-treema-view' {me} = require 'core/auth' +require 'vendor/treema' module.exports = class JobProfileTreemaView extends CocoView id: 'job-profile-view' diff --git a/app/views/account/PaymentsView.coffee b/app/views/account/PaymentsView.coffee index 077132a61..b355c4e42 100644 --- a/app/views/account/PaymentsView.coffee +++ b/app/views/account/PaymentsView.coffee @@ -9,10 +9,10 @@ module.exports = class PaymentsView extends RootView constructor: (options) -> super(options) - @payments = new CocoCollection([], { url: '/db/payment', model: Payment }) - @supermodel.loadCollection(@payments, 'payments') + @payments = new CocoCollection([], { url: '/db/payment', model: Payment, comparator:'_id' }) + @supermodel.loadCollection(@payments, 'payments', {cache: false}) getRenderData: -> c = super() c.payments = @payments - c \ No newline at end of file + c diff --git a/app/views/account/SubscriptionView.coffee b/app/views/account/SubscriptionView.coffee new file mode 100644 index 000000000..4fc341b45 --- /dev/null +++ b/app/views/account/SubscriptionView.coffee @@ -0,0 +1,329 @@ +RootView = require 'views/core/RootView' +template = require 'templates/account/subscription-view' +CocoCollection = require 'collections/CocoCollection' +SubscribeModal = require 'views/core/SubscribeModal' +Payment = require 'models/Payment' +stripeHandler = require 'core/services/stripe' +User = require 'models/User' +utils = require 'core/utils' + +# TODO: Link to sponsor id /user/userID instead of plain text name +# TODO: Link to sponsor email instead of plain text email +# TODO: Conslidate the multiple class for personal and recipient subscription info into 2 simple server API calls +# TODO: Track purchase amount based on actual users subscribed for a recipient subscribe event +# TODO: Validate email address formatting +# TODO: i18n pluralization for Stripe dialog description +# TODO: Don't prompt for new card if we have one already, just confirm purchase +# TODO: bulk discount isn't applied to personal sub +# TODO: next payment amount incorrect if have an expiring personal sub +# TODO: consider hiding managed subscription body UI while things are updating to avoid brief legacy data +# TODO: Next payment info for personal sub displays most recent payment when resubscribing before trial end +# TODO: PersonalSub and RecipientSubs have similar subscribe APIs +# TODO: Better recovery from trying to reuse a prepaid +# TODO: No way to unsubscribe from prepaid subscription +# TODO: Refactor state machines driving the UI. They've become a hot mess. + +# TODO: Get basic plan price dynamically +basicPlanPrice = 999 +basicPlanID = 'basic' + +module.exports = class SubscriptionView extends RootView + id: "subscription-view" + template: template + + events: + 'click .start-subscription-button': 'onClickStartSubscription' + 'click .end-subscription-button': 'onClickEndSubscription' + 'click .cancel-end-subscription-button': 'onClickCancelEndSubscription' + 'click .confirm-end-subscription-button': 'onClickConfirmEndSubscription' + 'click .recipients-subscribe-button': 'onClickRecipientsSubscribe' + 'click .confirm-recipient-unsubscribe-button': 'onClickRecipientConfirmUnsubscribe' + 'click .recipient-unsubscribe-button': 'onClickRecipientUnsubscribe' + + subscriptions: + 'subscribe-modal:subscribed': 'onSubscribed' + 'stripe:received-token': 'onStripeReceivedToken' + + constructor: (options) -> + super(options) + prepaidCode = utils.getQueryVariable '_ppc' + @personalSub = new PersonalSub(@supermodel, prepaidCode) + @recipientSubs = new RecipientSubs(@supermodel) + @personalSub.update => @render?() + @recipientSubs.update => @render?() + + getRenderData: -> + c = super() + c.personalSub = @personalSub + c.recipientSubs = @recipientSubs + c + + # Personal Subscriptions + + onClickStartSubscription: (e) -> + if @personalSub.prepaidCode + @personalSub.subscribe(=> @render?()) + else + @openModalView new SubscribeModal() + window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'account subscription view' + + onSubscribed: -> + document.location.reload() + + onClickEndSubscription: (e) -> + window.tracker?.trackEvent 'Unsubscribe Start' + @$el.find('.end-subscription-button').blur().addClass 'disabled', 250 + @$el.find('.unsubscribe-feedback').show(500).find('textarea').focus() + + onClickCancelEndSubscription: (e) -> + window.tracker?.trackEvent 'Unsubscribe Cancel' + @$el.find('.unsubscribe-feedback').hide(500).find('textarea').blur() + @$el.find('.end-subscription-button').focus().removeClass 'disabled', 250 + + onClickConfirmEndSubscription: (e) -> + message = @$el.find('.unsubscribe-feedback textarea').val().trim() + @personalSub.unsubscribe(message) + + # Sponsored subscriptions + + onClickRecipientsSubscribe: (e) -> + emails = @$el.find('.recipient-emails').val().split('\n') + @recipientSubs.startSubscribe(emails) + + onClickRecipientUnsubscribe: (e) -> + $(e.target).addClass('hide') + $(e.target).parent().find('.confirm-recipient-unsubscribe-button').removeClass('hide') + + onClickRecipientConfirmUnsubscribe: (e) -> + email = $(e.target).closest('tr').find('td.recipient-email').text() + @recipientSubs.unsubscribe(email, => @render?()) + + onStripeReceivedToken: (e) -> + @recipientSubs.finishSubscribe(e.token.id, => @render?()) + +# Helper classes for managing subscription actions and updating UI state + +class PersonalSub + constructor: (@supermodel, @prepaidCode) -> + + subscribe: (render) -> + return unless @prepaidCode + + if @prepaidCode is me.get('stripe')?.prepaidCode + delete @prepaidCode + return render() + + @state = 'subscribing' + @stateMessage = '' + render() + + stripeInfo = _.clone(me.get('stripe') ? {}) + stripeInfo.planID = basicPlanID + stripeInfo.prepaidCode = @prepaidCode + me.set('stripe', stripeInfo) + + me.once 'sync', => + application.tracker?.trackEvent 'Finished subscription purchase', value: 0 + delete @prepaidCode + @update(render) + me.once 'error', (user, response, options) => + console.error 'We got an error subscribing with Stripe from our server:', response + stripeInfo = me.get('stripe') ? {} + delete stripeInfo.planID + delete stripeInfo.prepaidCode + me.set('stripe', stripeInfo) + xhr = options.xhr + if xhr.status is 402 + @state = 'declined' + @stateMessage = '' + else + if xhr.status is 403 + delete @prepaidCode + @state = 'unknown_error' + @stateMessage = "#{xhr.status}: #{xhr.responseText}" + render() + me.patch({headers: {'X-Change-Plan': 'true'}}) + + unsubscribe: (message) -> + removeStripe = => + stripeInfo = _.clone(me.get('stripe')) + delete stripeInfo.planID + me.set('stripe', stripeInfo) + me.once 'sync', -> + window.tracker?.trackEvent 'Unsubscribe End', message: message + document.location.reload() + me.patch({headers: {'X-Change-Plan': 'true'}}) + if message + $.post '/contact', message: message, subject: 'Cancellation', (response) -> + removeStripe() + else + removeStripe() + + update: (render) -> + return unless stripeInfo = me.get('stripe') + + @state = 'loading' + + if stripeInfo.sponsorID + @sponsor = true + onSubSponsorSuccess = (sponsorInfo) => + @sponsorEmail = sponsorInfo.email + @sponsorName = sponsorInfo.name + @sponsorID = stripeInfo.sponsorID + if sponsorInfo.subscription.cancel_at_period_end + @endDate = new Date(sponsorInfo.subscription.current_period_end * 1000) + delete @state + render() + @supermodel.addRequestResource('sub_sponsor', { + url: '/db/user/-/sub_sponsor' + method: 'POST' + success: onSubSponsorSuccess + }, 0).load() + + else if stripeInfo.prepaidCode + @usingPrepaidCode = true + delete @state + render() + + else if stripeInfo.subscriptionID + @self = true + @active = me.isPremium() + @subscribed = stripeInfo.planID? + + options = { cache: false, url: "/db/user/#{me.id}/stripe" } + options.success = (info) => + if card = info.card + @card = "#{card.brand}: x#{card.last4}" + if sub = info.subscription + periodEnd = new Date((sub.trial_end or sub.current_period_end) * 1000) + if sub.cancel_at_period_end + @activeUntil = periodEnd + else if sub.discount?.coupon?.id isnt 'free' + @nextPaymentDate = periodEnd + @cost = "$#{(sub.plan.amount/100).toFixed(2)}" + else + console.error "Could not find personal subscription #{me.get('stripe')?.customerID} #{me.get('stripe')?.subscriptionID}" + delete @state + render() + @supermodel.addRequestResource('personal_payment_info', options).load() + + payments = new CocoCollection([], { url: '/db/payment', model: Payment, comparator:'_id' }) + payments.once 'sync', -> + @monthsSubscribed = (x for x in payments.models when not x.get('productID')).length + render() + @supermodel.loadCollection(payments, 'payments', {cache: false}) + + else if stripeInfo.free + @free = stripeInfo.free + delete @state + render() + + else + delete @state + render() + +class RecipientSubs + constructor: (@supermodel) -> + @recipients = {} + @unsubscribingRecipients = [] + + addSubscribing: (email) -> + @unsubscribingRecipients.push email + + removeSubscribing: (email) -> + _.remove(@unsubscribingRecipients, (recipientEmail) -> recipientEmail is email) + + startSubscribe: (emails) -> + @recipientEmails = (email.trim().toLowerCase() for email in emails) + _.remove(@recipientEmails, (email) -> _.isEmpty(email)) + return if @recipientEmails.length < 1 + + window.tracker?.trackEvent 'Start sponsored subscription' + + # TODO: this sometimes shows a rounded amount (e.g. $8.00) + currentSubCount = me.get('stripe')?.recipients?.length ? 0 + newSubCount = @recipientEmails.length + currentSubCount + amount = utils.getSponsoredSubsAmount(basicPlanPrice, newSubCount, me.get('stripe')?.subscriptionID?) - utils.getSponsoredSubsAmount(basicPlanPrice, currentSubCount, me.get('stripe')?.subscriptionID?) + options = { + description: "#{@recipientEmails.length} " + $.i18n.t('subscribe.stripe_description', defaultValue: 'Monthly Subscriptions') + amount: amount + alipay: if me.get('chinaVersion') or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto' + alipayReusable: true + } + @state = 'start subscribe' + @stateMessage = '' + stripeHandler.open(options) + + finishSubscribe: (tokenID, render) -> + return unless @state is 'start subscribe' # Don't intercept personal subcribe process + + @state = 'subscribing' + @stateMessage = '' + @justSubscribed = [] + render() + + stripeInfo = _.clone(me.get('stripe') ? {}) + stripeInfo.token = tokenID + stripeInfo.subscribeEmails = @recipientEmails + me.set('stripe', stripeInfo) + + me.once 'sync', => + application.tracker?.trackEvent 'Finished sponsored subscription purchase' + @update(render) + me.once 'error', (user, response, options) => + console.error 'We got an error subscribing with Stripe from our server:', response + stripeInfo = me.get('stripe') ? {} + delete stripeInfo.token + xhr = options.xhr + if xhr.status is 402 + @state = 'declined' + @stateMessage = '' + else + @state = 'unknown_error' + @stateMessage = "#{xhr.status}: #{xhr.responseText}" + render() + me.patch({headers: {'X-Change-Plan': 'true'}}) + + unsubscribe: (email, render) -> + delete @state + @stateMessage = '' + delete @justSubscribed + @addSubscribing(email) + render() + stripeInfo = _.clone(me.get('stripe')) + stripeInfo.unsubscribeEmail = email + me.set('stripe', stripeInfo) + me.once 'sync', => + @removeSubscribing(email) + @update(render) + me.patch({headers: {'X-Change-Plan': 'true'}}) + + update: (render) -> + delete @state + delete @stateMessage + return unless me.get('stripe')?.recipients + @unsubscribingRecipients = [] + + options = { cache: false, url: "/db/user/#{me.id}/stripe" } + options.success = (info) => + @sponsorSub = info.sponsorSubscription + if card = info.card + @card = "#{card.brand}: x#{card.last4}" + render() + @supermodel.addRequestResource('recipients_payment_info', options).load() + + onSubRecipientsSuccess = (recipientsMap) => + @recipients = recipientsMap + count = 0 + for userID, recipient of @recipients + count++ unless recipient.cancel_at_period_end + if @recipientEmails? and @justSubscribed? and recipient.emailLower in @recipientEmails + @justSubscribed.push recipient.emailLower + @nextPaymentAmount = utils.getSponsoredSubsAmount(basicPlanPrice, count, me.get('stripe')?.subscriptionID?) + @recipientEmails = [] + render() + @supermodel.addRequestResource('sub_recipients', { + url: '/db/user/-/sub_recipients' + method: 'POST' + success: onSubRecipientsSuccess + }, 0).load() diff --git a/app/views/account/UnsubscribeView.coffee b/app/views/account/UnsubscribeView.coffee index 3a056bff8..b098a9a38 100644 --- a/app/views/account/UnsubscribeView.coffee +++ b/app/views/account/UnsubscribeView.coffee @@ -1,5 +1,5 @@ RootView = require 'views/core/RootView' -template = require 'templates/account/unsubscribe' +template = require 'templates/account/unsubscribe-view' {me} = require 'core/auth' module.exports = class UnsubscribeView extends RootView @@ -25,11 +25,11 @@ module.exports = class UnsubscribeView extends RootView success = => @$el.find('.progress').hide() @$el.find('#success-alert').show() - me.fetch() + me.fetch cache: false error = => @$el.find('.progress').hide() @$el.find('#fail-alert').show() @$el.find('#unsubscribe-button').show() - $.ajax { url: url, success: success, error: error } + $.ajax { url: url, success: success, error: error, cache: false } diff --git a/app/views/account/WizardSettingsView.coffee b/app/views/account/WizardSettingsView.coffee deleted file mode 100644 index cf7599fb7..000000000 --- a/app/views/account/WizardSettingsView.coffee +++ /dev/null @@ -1,113 +0,0 @@ -CocoView = require 'views/core/CocoView' -template = require 'templates/account/wizard_settings' -{me} = require 'core/auth' -ThangType = require 'models/ThangType' -SpriteBuilder = require 'lib/sprites/SpriteBuilder' -{hslToHex, hexToHSL} = require 'core/utils' - -module.exports = class WizardSettingsView extends CocoView - id: 'wizard-settings-view' - template: template - - events: - 'click .color-group': (e) -> - return if $(e.target).closest('.minicolors')[0] - return if $(e.target).closest('.color-group-checkbox')[0] - return if $(e.target).closest('label')[0] - $(e.target).closest('.color-group').find('.color-group-checkbox').click() - 'change .color-group-checkbox': (e) -> - colorGroup = $(e.target).closest('.color-group') - @updateColorSettings(colorGroup) - @updateSwatchVisibility(colorGroup) - - constructor: -> - super(arguments...) - @loadWizard() - - loadWizard: -> - @wizardThangType = new ThangType() - @wizardThangType.setURL '/db/thang.type/wizard' - @supermodel.loadModel @wizardThangType, 'wizard' - - onLoaded: -> - super() - @spriteBuilder = new SpriteBuilder(@wizardThangType) - @initStage() - - getRenderData: -> - c = super() - wizardSettings = me.get('wizard')?.colorConfig or {} - - colorGroups = @wizardThangType.get('colorGroups') or {} - f = (name) -> - hslObj = wizardSettings[name] - hsl = if hslObj then [hslObj.hue, hslObj.saturation, hslObj.lightness] else [0, 0.5, 0.5] - return { - dasherized: _.string.dasherize(name) - humanized: _.string.humanize name - name: name - exists: wizardSettings[name] - rgb: hslToHex(hsl) - } - c.colorGroups = (f(colorName) for colorName in _.keys colorGroups when colorName isnt 'team') - c - - afterRender: -> - return unless @supermodel.finished() - wizardSettings = me.get('wizard') or {} - wizardSettings.colorConfig ?= {} - - @$el.find('.minicolors').each (e, minicolor) => - $(minicolor).minicolors({ - change: => @updateColorSettings($(minicolor).closest('.color-group')) - changeDelay: 200 - }) - - @$el.find('.color-group').each (i, colorGroup) => - @updateSwatchVisibility($(colorGroup)) - - updateSwatchVisibility: (colorGroup) -> - enabled = colorGroup.find('.color-group-checkbox').prop('checked') - colorGroup.find('.minicolors-swatch').toggle Boolean(enabled) - - updateColorSettings: (colorGroup) => - wizardSettings = $.extend(true, {}, me.get('wizard')) or {} - wizardSettings.colorConfig ?= {} - colorName = colorGroup.data('name') - wizardSettings.colorConfig[colorName] ?= {} - if colorGroup.find('.color-group-checkbox').prop('checked') - input = colorGroup.find('.minicolors-input') - hex = input.val() - hsl = hexToHSL(hex) - config = {hue: hsl[0], saturation: hsl[1], lightness: hsl[2]} - wizardSettings.colorConfig[colorName] = config - else - delete wizardSettings.colorConfig[colorName] - - me.set('wizard', wizardSettings) - @updateMovieClip() - @trigger 'change' - - initStage: -> - @stage = new createjs.Stage(@$el.find('canvas')[0]) - @updateMovieClip() - - updateMovieClip: -> - return unless @wizardThangType.loaded - wizardSettings = me.get('wizard') or {} - wizardSettings.colorConfig ?= {} - - @stage.removeChild(@movieClip) if @movieClip - options = {colorConfig: wizardSettings.colorConfig} - @spriteBuilder.setOptions options - @spriteBuilder.buildColorMaps() - castAction = @wizardThangType.get('actions')?.cast - return unless castAction?.animation - @movieClip = @spriteBuilder.buildMovieClip castAction.animation - @movieClip.scaleY = @movieClip.scaleX = 1.7 * (castAction.scale or 1) - reg = castAction.positions?.registration - if reg - @movieClip.regX = reg.x - @movieClip.regY = reg.y - @stage.addChild @movieClip - @stage.update() diff --git a/app/views/admin/AdministerUserModal.coffee b/app/views/admin/AdministerUserModal.coffee new file mode 100644 index 000000000..171fae00e --- /dev/null +++ b/app/views/admin/AdministerUserModal.coffee @@ -0,0 +1,60 @@ +ModalView = require 'views/core/ModalView' +template = require 'templates/admin/administer-user-modal' +User = require 'models/User' + +module.exports = class AdministerUserModal extends ModalView + id: "administer-user-modal" + template: template + plain: true + + events: + 'click #save-changes': 'onSaveChanges' + + constructor: (options, @userHandle) -> + super(options) + @user = @supermodel.loadModel(new User({_id:@userHandle}), 'user', {cache: false}).model + options = {cache: false, url: '/stripe/coupons'} + options.success = (@coupons) => + @couponsResource = @supermodel.addRequestResource('coupon', options) + @couponsResource.load() + + getRenderData: -> + c = super() + stripe = @user.get('stripe') or {} + c.free = stripe.free is true + c.freeUntil = _.isString(stripe.free) + c.freeUntilDate = if c.freeUntil then stripe.free else new Date().toISOString()[...10] + c.coupon = stripe.couponID + c.coupons = @coupons or [] + for coupon in c.coupons + bits = [coupon.id] + if coupon.percent_off + bits.push "(#{coupon.percent_off}% off)" + else if coupon.amount_off + bits.push "($#{coupon.amount_off} off)" + if coupon.duration + bits.push "(duration: #{coupon.duration})" + if coupon.redeem_by + bits.push "(redeem by: #{moment(coupon.redeem_by).format('lll')}" + coupon.format = bits.join(' ') + c.none = not (c.free or c.freeUntil or c.coupon) + c.user = @user + c + + onSaveChanges: -> + stripe = _.clone(@user.get('stripe') or {}) + delete stripe.free + delete stripe.couponID + + selection = @$el.find('input[name="stripe-benefit"]:checked').val() + dateVal = @$el.find('#free-until-date').val() + couponVal = @$el.find('#coupon-select').val() + switch selection + when 'free' then stripe.free = true + when 'free-until' then stripe.free = dateVal + when 'coupon' then stripe.couponID = couponVal + + @user.set('stripe', stripe) + options = {} + options.success = => @hide() + @user.patch(options) diff --git a/app/views/admin/AnalyticsSubscriptionsView.coffee b/app/views/admin/AnalyticsSubscriptionsView.coffee new file mode 100644 index 000000000..3d938acce --- /dev/null +++ b/app/views/admin/AnalyticsSubscriptionsView.coffee @@ -0,0 +1,763 @@ +RootView = require 'views/core/RootView' +template = require 'templates/admin/analytics-subscriptions' +ThangType = require 'models/ThangType' +User = require 'models/User' + +# TODO: Graphing code copied/mangled from campaign editor level view. OMG, DRY. + +require 'vendor/d3' + +module.exports = class AnalyticsSubscriptionsView extends RootView + id: 'admin-analytics-subscriptions-view' + template: template + targetSubCount: 1200 + + constructor: (options) -> + super options + @resetSubscriptionsData() + if me.isAdmin() + @refreshData() + _.delay (=> @refreshData()), 30 * 60 * 1000 + + getRenderData: -> + context = super() + context.analytics = @analytics ? graphs: [] + context.cancellations = @cancellations ? [] + context.subs = _.cloneDeep(@subs ? []).reverse() + context.subscribers = @subscribers ? [] + context.subscriberCancelled = _.find context.subscribers, (subscriber) -> subscriber.cancel + context.subscriberSponsored = _.find context.subscribers, (subscriber) -> subscriber.user?.stripe?.sponsorID + context.total = @total ? 0 + context.cancelled = @cancellations?.length ? @cancelled ? 0 + context.monthlyChurn = @monthlyChurn ? 0.0 + context.monthlyGrowth = @monthlyGrowth ? 0.0 + context.outstandingCancels = @outstandingCancels ? [] + context.refreshDataState = @refreshDataState + context + + afterRender: -> + super() + @updateAnalyticsGraphs() + + resetSubscriptionsData: -> + @analytics = graphs: [] + @subs = [] + @total = 0 + @cancelled = 0 + @monthlyChurn = 0.0 + @monthlyGrowth = 0.0 + @refreshDataState = 'Fetching dashboard data...' + + refreshData: -> + return unless me.isAdmin() + @resetSubscriptionsData() + @getCancellations (cancellations) => + @cancellations = cancellations + @render?() + @getOutstandingCancelledSubscriptions cancellations, (outstandingCancels) => + @outstandingCancels = outstandingCancels + @getSubscriptions cancellations, (subscriptions) => + @updateAnalyticsGraphData() + @render?() + @getSubscribers subscriptions, => + @render?() + + updateFetchDataState: (msg) -> + @refreshDataState = msg + @render?() + + getCancellations: (done) -> + cancellations = [] + @getCancellationEvents (cancelledSubscriptions) => + # Get user objects for cancelled subscriptions + userIDs = _.map cancelledSubscriptions, (a) -> a.userID + options = + url: '/db/user/-/users' + method: 'POST' + data: {ids: userIDs} + options.error = (model, response, options) => + return if @destroyed + console.error 'Failed to get cancelled users', response + options.success = (cancelledUsers, response, options) => + return if @destroyed + userMap = {} + userMap[user._id] = user for user in cancelledUsers + for cancellation in cancelledSubscriptions when cancellation.userID of userMap + cancellation.user = userMap[cancellation.userID] + cancellation.level = User.levelFromExp(cancellation.user.points) + cancelledSubscriptions.sort (a, b) -> if a.cancel > b.cancel then -1 else 1 + done(cancelledSubscriptions) + @updateFetchDataState 'Fetching cancellations...' + @supermodel.addRequestResource('get_cancelled_users', options, 0).load() + + getCancellationEvents: (done) -> + cancellationEvents = [] + earliestEventDate = new Date() + earliestEventDate.setUTCMonth(earliestEventDate.getUTCMonth() - 1) + earliestEventDate.setUTCDate(earliestEventDate.getUTCDate() - 8) + nextBatch = (starting_after, done) => + @updateFetchDataState "Fetching cancellations #{cancellationEvents.length}..." + options = + url: '/db/subscription/-/stripe_events' + method: 'POST' + data: {options: {limit: 100}} + options.data.options.starting_after = starting_after if starting_after + options.data.options.type = 'customer.subscription.updated' + options.data.options.created = gte: Math.floor(earliestEventDate.getTime() / 1000) + options.error = (model, response, options) => + return if @destroyed + console.error 'Failed to get cancelled events', response + options.success = (events, response, options) => + return if @destroyed + for event in events.data + continue unless event.data?.object?.cancel_at_period_end is true and event.data?.previous_attributes.cancel_at_period_end is false + continue unless event.data?.object?.plan?.id is 'basic' + continue unless event.data?.object?.id? + cancellationEvents.push + cancel: new Date(event.created * 1000) + customerID: event.data.object.customer + start: new Date(event.data.object.start * 1000) + subscriptionID: event.data.object.id + userID: event.data.object.metadata?.id + + if events.has_more + return nextBatch(events.data[events.data.length - 1].id, done) + done(cancellationEvents) + @supermodel.addRequestResource('get_cancellation_events', options, 0).load() + nextBatch null, done + + getOutstandingCancelledSubscriptions: (cancellations, done) -> + @updateFetchDataState "Fetching oustanding cancellations..." + options = + url: '/db/subscription/-/stripe_subscriptions' + method: 'POST' + data: {subscriptions: cancellations} + options.error = (model, response, options) => + return if @destroyed + console.error 'Failed to get outstanding cancellations', response + options.success = (subscriptions, response, options) => + return if @destroyed + outstandingCancelledSubscriptions = [] + for subscription in subscriptions + continue unless subscription?.cancel_at_period_end + outstandingCancelledSubscriptions.push + cancel: new Date(subscription.canceled_at * 1000) + customerID: subscription.customerID + start: new Date(subscription.start * 1000) + subscriptionID: subscription.id + userID: subscription.metadata?.id + done(outstandingCancelledSubscriptions) + @supermodel.addRequestResource('get_outstanding_cancelled_subscriptions', options, 0).load() + + getSubscribers: (subscriptions, done) -> + # console.log 'getSubscribers', subscriptions.length + @updateFetchDataState "Fetching recent subscribers..." + @render?() + maxSubscribers = 40 + + subscribers = _.filter subscriptions, (a) -> a.userID? + subscribers.sort (a, b) -> if a.start > b.start then -1 else 1 + subscribers = subscribers.slice(0, maxSubscribers) + subscriberUserIDs = _.map subscribers, (a) -> a.userID + + options = + url: '/db/subscription/-/subscribers' + method: 'POST' + data: {ids: subscriberUserIDs} + options.error = (model, response, options) => + return if @destroyed + console.error 'Failed to get subscribers', response + options.success = (userMap, response, options) => + return if @destroyed + for subscriber in subscribers + continue unless subscriber.userID of userMap + subscriber.user = userMap[subscriber.userID] + subscriber.level = User.levelFromExp subscriber.user.points + if hero = subscriber.user.heroConfig?.thangType + subscriber.hero = _.invert(ThangType.heroes)[hero] + @subscribers = subscribers + done() + @supermodel.addRequestResource('get_subscribers', options, 0).load() + + getSubscriptions: (cancellations=[], done) -> + @getInvoices (invoices) => + subMap = {} + for invoice in invoices + subID = invoice.subscriptionID + if subID of subMap + subMap[subID].first = new Date(invoice.date) + else + subMap[subID] = + first: new Date(invoice.date) + last: new Date(invoice.date) + customerID: invoice.customerID + subMap[subID].userID = invoice.userID if invoice.userID + + @getSponsors (sponsors) => + @getRecipientSubscriptions sponsors, (recipientSubscriptions) => + for subscription in recipientSubscriptions + subMap[subscription.id] = + first: new Date(subscription.start * 1000) + subMap[subscription.id].userID = subscription.metadata.id if subscription.metadata?.id? + if subscription.cancel_at_period_end + subMap[subscription.id].cancel = new Date(subscription.canceled_at * 1000) + subMap[subscription.id].end = new Date(subscription.current_period_end * 1000) + + subs = [] + for subID of subMap + sub = + customerID: subMap[subID].customerID + start: subMap[subID].first + subscriptionID: subID + sub.cancel = subMap[subID].cancel if subMap[subID].cancel + oneMonthAgo = new Date() + oneMonthAgo.setUTCMonth(oneMonthAgo.getUTCMonth() - 1) + if subMap[subID].end? + sub.end = subMap[subID].end + else if subMap[subID].last < oneMonthAgo + sub.end = subMap[subID].last + sub.end.setUTCMonth(sub.end.getUTCMonth() + 1) + sub.userID = subMap[subID].userID if subMap[subID].userID + subs.push sub + + subDayMap = {} + for sub in subs + startDay = sub.start.toISOString().substring(0, 10) + subDayMap[startDay] ?= {} + subDayMap[startDay]['start'] ?= 0 + subDayMap[startDay]['start']++ + if endDay = sub?.end?.toISOString().substring(0, 10) + subDayMap[endDay] ?= {} + subDayMap[endDay]['end'] ?= 0 + subDayMap[endDay]['end']++ + for cancellation in cancellations + if cancellation.subscriptionID is sub.subscriptionID + sub.cancel = cancellation.cancel + cancelDay = cancellation.cancel.toISOString().substring(0, 10) + subDayMap[cancelDay] ?= {} + subDayMap[cancelDay]['cancel'] ?= 0 + subDayMap[cancelDay]['cancel']++ + break + + today = new Date().toISOString().substring(0, 10) + for day of subDayMap + continue if day > today + @subs.push + day: day + started: subDayMap[day]['start'] or 0 + cancelled: subDayMap[day]['cancel'] or 0 + ended: subDayMap[day]['end'] or 0 + + @subs.sort (a, b) -> a.day.localeCompare(b.day) + totalLastMonth = 0 + for sub, i in @subs + @total += sub.started + @total -= sub.ended + @cancelled += sub.cancelled + sub.total = @total + totalLastMonth = @total if @subs.length - i is 31 + @monthlyChurn = @cancelled / totalLastMonth * 100.0 if totalLastMonth > 0 + if @subs.length > 30 and @subs[@subs.length - 31].total > 0 + startMonthTotal = @subs[@subs.length - 31].total + endMonthTotal = @subs[@subs.length - 1].total + @monthlyGrowth = (endMonthTotal / startMonthTotal - 1) * 100 + done(subs) + + getInvoices: (done) -> + invoices = {} + + addInvoice = (invoice) => + return unless invoice.paid + return unless invoice.subscription + return unless invoice.total > 0 + return unless invoice.lines?.data?[0]?.plan?.id is 'basic' + invoices[invoice.id] = + customerID: invoice.customer + subscriptionID: invoice.subscription + date: new Date(invoice.date * 1000) + invoices[invoice.id].userID = invoice.lines.data[0].metadata.id if invoice.lines?.data?[0]?.metadata?.id + + getLiveInvoices = (ending_before, done) => + + nextBatch = (ending_before, done) => + @updateFetchDataState "Fetching invoices #{Object.keys(invoices).length}..." + options = + url: '/db/subscription/-/stripe_invoices' + method: 'POST' + data: {options: {ending_before: ending_before, limit: 100}} + options.error = (model, response, options) => + return if @destroyed + console.error 'Failed to get live invoices', response + options.success = (invoiceData, response, options) => + return if @destroyed + addInvoice(invoice) for invoice in invoiceData.data + if invoiceData.has_more + return nextBatch(invoiceData.data[0].id, done) + else + invoices = (invoice for invoiceID, invoice of invoices) + invoices.sort (a, b) -> if a.date > b.date then -1 else 1 + return done(invoices) + @supermodel.addRequestResource('get_live_invoices', options, 0).load() + + nextBatch ending_before, done + + getAnalyticsInvoices = (done) => + @updateFetchDataState "Fetching invoices #{Object.keys(invoices).length}..." + options = + url: '/db/analytics_stripe_invoice/-/all' + method: 'GET' + options.error = (model, response, options) => + return if @destroyed + console.error 'Failed to get analytics stripe invoices', response + options.success = (docs, response, options) => + return if @destroyed + docs.sort (a, b) -> b.date - a.date + addInvoice(doc.properties) for doc in docs + getLiveInvoices(docs[0]._id, done) + @supermodel.addRequestResource('get_analytics_invoices', options, 0).load() + + getAnalyticsInvoices(done) + + getRecipientSubscriptions: (sponsors, done) -> + @updateFetchDataState "Fetching recipient subscriptions..." + subscriptionsToFetch = [] + for user in sponsors + for recipient in user.stripe?.recipients + subscriptionsToFetch.push + customerID: user.stripe.customerID + subscriptionID: recipient.subscriptionID + return done([]) if _.isEmpty subscriptionsToFetch + options = + url: '/db/subscription/-/stripe_subscriptions' + method: 'POST' + data: {subscriptions: subscriptionsToFetch} + options.error = (model, response, options) => + return if @destroyed + console.error 'Failed to get recipient subscriptions', response + options.success = (subscriptions, response, options) => + return if @destroyed + done(subscriptions) + @supermodel.addRequestResource('get_recipient_subscriptions', options, 0).load() + + getSponsors: (done) -> + @updateFetchDataState "Fetching sponsors..." + options = + url: '/db/user/-/sub_sponsors' + method: 'POST' + options.error = (model, response, options) => + return if @destroyed + console.error 'Failed to get sponsors', response + options.success = (sponsors, response, options) => + return if @destroyed + done(sponsors) + @supermodel.addRequestResource('get_sponsors', options, 0).load() + + updateAnalyticsGraphData: -> + # console.log 'updateAnalyticsGraphData' + # Build graphs based on available @analytics data + # Currently only one graph + @analytics.graphs = [] + + return unless @subs?.length > 0 + + @addGraphData(60) + @addGraphData(180, true) + + addGraphData: (timeframeDays, skipCancelled=false) -> + graph = {graphID: 'total-subs', lines: []} + + # TODO: Where should this metadata live? + # TODO: lineIDs assumed to be unique across graphs + totalSubsID = 'total-subs' + targetSubsID = 'target-subs' + startedSubsID = 'started-subs' + cancelledSubsID = 'cancelled-subs' + netSubsID = 'net-subs' + averageNewID = 'average-new' + lineMetadata = {} + lineMetadata[totalSubsID] = + description: 'Total Active Subscriptions' + color: 'green' + strokeWidth: 1 + lineMetadata[targetSubsID] = + description: 'Target Total Subscriptions' + color: 'gold' + strokeWidth: 4 + opacity: 1.0 + lineMetadata[startedSubsID] = + description: 'New Subscriptions' + color: 'blue' + strokeWidth: 1 + lineMetadata[cancelledSubsID] = + description: 'Cancelled Subscriptions' + color: 'red' + strokeWidth: 1 + lineMetadata[netSubsID] = + description: '7-day Average Net Subscriptions (started - cancelled)' + color: 'black' + strokeWidth: 4 + lineMetadata[averageNewID] = + description: '7-day Average New Subscriptions' + color: 'black' + strokeWidth: 4 + + days = (sub.day for sub in @subs) + if days.length > 0 + currentIndex = 0 + currentDay = days[currentIndex] + currentDate = new Date(currentDay + "T00:00:00.000Z") + lastDay = days[days.length - 1] + while currentDay isnt lastDay + days.splice currentIndex, 0, currentDay if days[currentIndex] isnt currentDay + currentIndex++ + currentDate.setUTCDate(currentDate.getUTCDate() + 1) + currentDay = currentDate.toISOString().substr(0, 10) + + ## Totals + + # Build line data + levelPoints = [] + for sub, i in @subs + levelPoints.push + x: i + y: sub.total + day: sub.day + pointID: "#{totalSubsID}#{i}" + values: [] + + # Ensure points for each day + for day, i in days + if levelPoints.length <= i or levelPoints[i].day isnt day + prevY = if i > 0 then levelPoints[i - 1].y else 0.0 + levelPoints.splice i, 0, + y: prevY + day: day + values: [] + levelPoints[i].x = i + levelPoints[i].pointID = "#{totalSubsID}#{i}" + + levelPoints.splice(0, levelPoints.length - timeframeDays) if levelPoints.length > timeframeDays + + graph.lines.push + lineID: totalSubsID + enabled: true + points: levelPoints + description: lineMetadata[totalSubsID].description + lineColor: lineMetadata[totalSubsID].color + strokeWidth: lineMetadata[totalSubsID].strokeWidth + min: 0 + max: Math.max(@targetSubCount, d3.max(@subs, (d) -> d.total)) + + ## Started + + # Build line data + levelPoints = [] + for sub, i in @subs + levelPoints.push + x: i + y: sub.started + day: sub.day + pointID: "#{startedSubsID}#{i}" + values: [] + + # Ensure points for each day + for day, i in days + if levelPoints.length <= i or levelPoints[i].day isnt day + prevY = if i > 0 then levelPoints[i - 1].y else 0.0 + levelPoints.splice i, 0, + y: prevY + day: day + values: [] + levelPoints[i].x = i + levelPoints[i].pointID = "#{startedSubsID}#{i}" + + levelPoints.splice(0, levelPoints.length - timeframeDays) if levelPoints.length > timeframeDays + + graph.lines.push + lineID: startedSubsID + enabled: true + points: levelPoints + description: lineMetadata[startedSubsID].description + lineColor: lineMetadata[startedSubsID].color + strokeWidth: lineMetadata[startedSubsID].strokeWidth + min: 0 + max: d3.max(@subs[-timeframeDays..], (d) -> d.started + 2) + + if skipCancelled + + ## 7-Day average started + + # Build line data + levelPoints = [] + sevenStarts = [] + for sub, i in @subs + average = 0 + sevenStarts.push sub.started + if sevenStarts.length > 7 + sevenStarts.shift() + if sevenStarts.length is 7 + average = sevenStarts.reduce((a, b) -> a + b) / sevenStarts.length + levelPoints.push + x: i + y: average + day: sub.day + pointID: "#{averageNewID}#{i}" + values: [] + + # Ensure points for each day + for day, i in days + if levelPoints.length <= i or levelPoints[i].day isnt day + prevY = if i > 0 then levelPoints[i - 1].y else 0.0 + levelPoints.splice i, 0, + y: prevY + day: day + values: [] + levelPoints[i].x = i + levelPoints[i].pointID = "#{averageNewID}#{i}" + + levelPoints.splice(0, levelPoints.length - timeframeDays) if levelPoints.length > timeframeDays + + graph.lines.push + lineID: averageNewID + enabled: true + points: levelPoints + description: lineMetadata[averageNewID].description + lineColor: lineMetadata[averageNewID].color + strokeWidth: lineMetadata[averageNewID].strokeWidth + min: 0 + max: d3.max(@subs[-timeframeDays..], (d) -> d.started + 2) + + else + + ## Total subs target + + # Build line data + levelPoints = [] + for sub, i in @subs + levelPoints.push + x: i + y: @targetSubCount + day: sub.day + pointID: "#{targetSubsID}#{i}" + values: [] + + levelPoints.splice(0, levelPoints.length - timeframeDays) if levelPoints.length > timeframeDays + + graph.lines.push + lineID: targetSubsID + enabled: true + points: levelPoints + description: lineMetadata[targetSubsID].description + lineColor: lineMetadata[targetSubsID].color + strokeWidth: lineMetadata[targetSubsID].strokeWidth + min: 0 + max: Math.max(@targetSubCount, d3.max(@subs, (d) -> d.total)) + + ## Cancelled + + # TODO: move this average cancelled stuff up the chain + averageCancelled = 0 + + # Build line data + levelPoints = [] + cancelled = [] + for sub, i in @subs[@subs.length - 30...] + cancelled.push sub.cancelled + levelPoints.push + x: @subs.length - 30 + i + y: sub.cancelled + day: sub.day + pointID: "#{cancelledSubsID}#{@subs.length - 30 + i}" + values: [] + averageCancelled = cancelled.reduce((a, b) -> a + b) / cancelled.length + for sub, i in @subs[0...-30] + levelPoints.splice i, 0, + x: i + y: averageCancelled + day: sub.day + pointID: "#{cancelledSubsID}#{i}" + values: [] + + # Ensure points for each day + for day, i in days + if levelPoints.length <= i or levelPoints[i].day isnt day + prevY = if i > 0 then levelPoints[i - 1].y else 0.0 + levelPoints.splice i, 0, + y: prevY + day: day + values: [] + levelPoints[i].x = i + levelPoints[i].pointID = "#{cancelledSubsID}#{i}" + + levelPoints.splice(0, levelPoints.length - timeframeDays) if levelPoints.length > timeframeDays + + graph.lines.push + lineID: cancelledSubsID + enabled: true + points: levelPoints + description: lineMetadata[cancelledSubsID].description + lineColor: lineMetadata[cancelledSubsID].color + strokeWidth: lineMetadata[cancelledSubsID].strokeWidth + min: 0 + max: d3.max(@subs[-timeframeDays..], (d) -> d.started + 2) + + ## 7-Day Net Subs + + # Build line data + levelPoints = [] + sevenNets = [] + for sub, i in @subs + net = 0 + if i >= @subs.length - 30 + sevenNets.push sub.started - sub.cancelled + else + sevenNets.push sub.started - averageCancelled + if sevenNets.length > 7 + sevenNets.shift() + if sevenNets.length is 7 + net = sevenNets.reduce((a, b) -> a + b) / 7 + levelPoints.push + x: i + y: net + day: sub.day + pointID: "#{netSubsID}#{i}" + values: [] + + # Ensure points for each day + for day, i in days + if levelPoints.length <= i or levelPoints[i].day isnt day + prevY = if i > 0 then levelPoints[i - 1].y else 0.0 + levelPoints.splice i, 0, + y: prevY + day: day + values: [] + levelPoints[i].x = i + levelPoints[i].pointID = "#{netSubsID}#{i}" + + levelPoints.splice(0, levelPoints.length - timeframeDays) if levelPoints.length > timeframeDays + + graph.lines.push + lineID: netSubsID + enabled: true + points: levelPoints + description: lineMetadata[netSubsID].description + lineColor: lineMetadata[netSubsID].color + strokeWidth: lineMetadata[netSubsID].strokeWidth + min: 0 + max: d3.max(@subs[-timeframeDays..], (d) -> d.started + 2) + + @analytics.graphs.push(graph) + + updateAnalyticsGraphs: -> + # Build d3 graphs + return unless @analytics?.graphs?.length > 0 + containerSelector = '.line-graph-container' + # console.log 'updateAnalyticsGraphs', containerSelector, @analytics.graphs + + margin = 20 + keyHeight = 20 + xAxisHeight = 20 + yAxisWidth = 40 + containerWidth = $(containerSelector).width() + containerHeight = $(containerSelector).height() + + for graph in @analytics.graphs + graphLineCount = _.reduce graph.lines, ((sum, item) -> if item.enabled then sum + 1 else sum), 0 + svg = d3.select(containerSelector).append("svg") + .attr("width", containerWidth) + .attr("height", containerHeight) + width = containerWidth - margin * 2 - yAxisWidth * 2 + height = containerHeight - margin * 2 - xAxisHeight - keyHeight * graphLineCount + currentLine = 0 + for line in graph.lines + continue unless line.enabled + xRange = d3.scale.linear().range([0, width]).domain([d3.min(line.points, (d) -> d.x), d3.max(line.points, (d) -> d.x)]) + yRange = d3.scale.linear().range([height, 0]).domain([line.min, line.max]) + + # x-Axis + if currentLine is 0 + startDay = new Date(line.points[0].day) + endDay = new Date(line.points[line.points.length - 1].day) + xAxisRange = d3.time.scale() + .domain([startDay, endDay]) + .range([0, width]) + xAxis = d3.svg.axis() + .scale(xAxisRange) + svg.append("g") + .attr("class", "x axis") + .call(xAxis) + .selectAll("text") + .attr("dy", ".35em") + .attr("transform", "translate(" + (margin + yAxisWidth) + "," + (height + margin) + ")") + .style("text-anchor", "start") + + if line.lineID is 'started-subs' + # Horizontal guidelines + marks = (Math.round(i * line.max / 5) for i in [1...5]) + svg.selectAll(".line") + .data(marks) + .enter() + .append("line") + .attr("x1", margin + yAxisWidth * 2) + .attr("y1", (d) -> margin + yRange(d)) + .attr("x2", margin + yAxisWidth * 2 + width) + .attr("y2", (d) -> margin + yRange(d)) + .attr("stroke", line.lineColor) + .style("opacity", "0.5") + + if currentLine < 2 + # y-Axis + yAxisRange = d3.scale.linear().range([height, 0]).domain([line.min, line.max]) + yAxis = d3.svg.axis() + .scale(yRange) + .orient("left") + svg.append("g") + .attr("class", "y axis") + .attr("transform", "translate(" + (margin + yAxisWidth * currentLine) + "," + margin + ")") + .style("color", line.lineColor) + .call(yAxis) + .selectAll("text") + .attr("y", 0) + .attr("x", 0) + .attr("fill", line.lineColor) + .style("text-anchor", "start") + + # Key + svg.append("line") + .attr("x1", margin) + .attr("y1", margin + height + xAxisHeight + keyHeight * currentLine + keyHeight / 2) + .attr("x2", margin + 40) + .attr("y2", margin + height + xAxisHeight + keyHeight * currentLine + keyHeight / 2) + .attr("stroke", line.lineColor) + .attr("class", "key-line") + svg.append("text") + .attr("x", margin + 40 + 10) + .attr("y", margin + height + xAxisHeight + keyHeight * currentLine + (keyHeight + 10) / 2) + .attr("fill", if line.lineColor is 'gold' then 'orange' else line.lineColor) + .attr("class", "key-text") + .text(line.description) + + # Path and points + svg.selectAll(".circle") + .data(line.points) + .enter() + .append("circle") + .attr("transform", "translate(" + (margin + yAxisWidth * 2) + "," + margin + ")") + .attr("cx", (d) -> xRange(d.x)) + .attr("cy", (d) -> yRange(d.y)) + .attr("r", 2) + .attr("fill", line.lineColor) + .attr("stroke-width", 1) + .attr("class", "graph-point") + .attr("data-pointid", (d) -> "#{line.lineID}#{d.x}") + d3line = d3.svg.line() + .x((d) -> xRange(d.x)) + .y((d) -> yRange(d.y)) + .interpolate("linear") + svg.append("path") + .attr("d", d3line(line.points)) + .attr("transform", "translate(" + (margin + yAxisWidth * 2) + "," + margin + ")") + .style("stroke-width", line.strokeWidth) + .style("stroke", line.lineColor) + .style("fill", "none") + currentLine++ diff --git a/app/views/admin/GrowthView.coffee b/app/views/admin/AnalyticsUsersView.coffee similarity index 95% rename from app/views/admin/GrowthView.coffee rename to app/views/admin/AnalyticsUsersView.coffee index c43319f03..063a8f6ab 100644 --- a/app/views/admin/GrowthView.coffee +++ b/app/views/admin/AnalyticsUsersView.coffee @@ -1,7 +1,9 @@ RootView = require 'views/core/RootView' -template = require 'templates/admin/growth' +template = require 'templates/admin/analytics-users' RealTimeCollection = require 'collections/RealTimeCollection' +require 'vendor/d3' + # Growth View ################### # # Display interesting growth data. @@ -14,8 +16,8 @@ RealTimeCollection = require 'collections/RealTimeCollection' # TODO: aggregate recent data if missing? # -module.exports = class GrowthView extends RootView - id: 'admin-growth-view' +module.exports = class AnalyticsUsersView extends RootView + id: 'admin-analytics-users-view' template: template height: 300 width: 1000 @@ -53,7 +55,7 @@ module.exports = class GrowthView extends RootView if me.isAdmin() @createPerDayChart() @createPerMonthChart() - + createPerDayChart: -> addedData = [] totalData = [] @@ -74,7 +76,7 @@ module.exports = class GrowthView extends RootView createLineChart: (selector, data, guidelineSpacing, sevenDayAverage=false) -> return unless data.length > 1 - + minVal = d3.min(data, (d) -> d.value) maxVal = d3.max(data, (d) -> d.value) @@ -127,16 +129,16 @@ module.exports = class GrowthView extends RootView .attr("cx", (d) -> d.x ) .attr("cy", (d) -> d.y ) .attr("r", "2px") - .attr("fill", "black") - + .attr("fill", "black") + chart.selectAll(".text") .data(points) .enter() .append("text") .attr("dy", ".35em") .attr("transform", (d, i) => "translate(" + d.x + "," + @height + ") rotate(270)") - .text((d) -> - if d.id.length is 8 + .text((d) -> + if d.id.length is 8 return "#{parseInt(d.id[4..5])}/#{parseInt(d.id[6..7])}/#{d.id[0..3]}" else return "#{parseInt(d.id[4..5])}/#{d.id[0..3]}" @@ -159,7 +161,7 @@ module.exports = class GrowthView extends RootView .attr("cx", (d) -> d.x ) .attr("cy", (d) -> d.y ) .attr("r", "2px") - .attr("fill", "purple") + .attr("fill", "purple") chart.selectAll('.line') .data(sevenLinks) @@ -189,4 +191,3 @@ module.exports = class GrowthView extends RootView .attr("y", (d) -> d.start.y - 6) .attr("dy", ".35em") .text((d) -> d.start.id) - diff --git a/app/views/admin/CLAsView.coffee b/app/views/admin/CLAsView.coffee index 19f99447d..45af0ce46 100644 --- a/app/views/admin/CLAsView.coffee +++ b/app/views/admin/CLAsView.coffee @@ -18,7 +18,7 @@ module.exports = class CLAsView extends RootView constructor: (options) -> super options - @clas = @supermodel.loadCollection(new CLACollection(), 'clas').model + @clas = @supermodel.loadCollection(new CLACollection(), 'clas', {cache: false}).model getRenderData: -> c = super() diff --git a/app/views/admin/CandidatesView.coffee b/app/views/admin/CandidatesView.coffee index 81405c0f6..c8a5380a6 100644 --- a/app/views/admin/CandidatesView.coffee +++ b/app/views/admin/CandidatesView.coffee @@ -23,7 +23,8 @@ module.exports = class CandidatesView extends RootView constructor: (options) -> super options - @candidates = @supermodel.loadCollection(new CandidatesCollection(), 'candidates').model + #@candidates = @supermodel.loadCollection(new CandidatesCollection(), 'candidates').model + @candidates = models: [] # Disabling, since we got rid of the index that fetches these. @remarks = @supermodel.loadCollection(new UserRemarksCollection(), 'user_remarks').model onLoaded: -> diff --git a/app/views/admin/FilesView.coffee b/app/views/admin/FilesView.coffee index 2301b5797..8867080fe 100644 --- a/app/views/admin/FilesView.coffee +++ b/app/views/admin/FilesView.coffee @@ -37,6 +37,7 @@ module.exports = class FilesView extends RootView $.ajax url: "/file/#{@currentFolder()}/" success: @onLoadedFiles + cache: false onLoadedFiles: (res) => table = tableTemplate({files:res}) diff --git a/app/views/admin/LevelSessionsView.coffee b/app/views/admin/LevelSessionsView.coffee index 816e869d0..26b8baa3c 100644 --- a/app/views/admin/LevelSessionsView.coffee +++ b/app/views/admin/LevelSessionsView.coffee @@ -16,7 +16,7 @@ module.exports = class LevelSessionsView extends RootView @getLevelSessions() getLevelSessions: -> - @sessions = @supermodel.loadCollection(new LevelSessionCollection(), 'sessions').model + @sessions = @supermodel.loadCollection(new LevelSessionCollection(), 'sessions', {cache: false}).model getRenderData: => c = super() diff --git a/app/views/admin/MainAdminView.coffee b/app/views/admin/MainAdminView.coffee index f35bfe1eb..33a00bd30 100644 --- a/app/views/admin/MainAdminView.coffee +++ b/app/views/admin/MainAdminView.coffee @@ -1,6 +1,7 @@ {backboneFailure, genericFailure} = require 'core/errors' RootView = require 'views/core/RootView' template = require 'templates/admin' +AdministerUserModal = require 'views/admin/AdministerUserModal' module.exports = class MainAdminView extends RootView id: 'admin-view' @@ -12,6 +13,13 @@ module.exports = class MainAdminView extends RootView 'click #enter-espionage-mode': 'enterEspionageMode' 'click #user-search-button': 'searchForUser' 'click #increment-button': 'incrementUserAttribute' + 'click #user-search-result': 'onClickUserSearchResult' + 'click #create-free-sub-btn': 'onClickFreeSubLink' + + getRenderData: -> + context = super() + context.freeSubLink = @freeSubLink + context checkForFormSubmissionEnterPress: (e) -> if e.which is 13 and @$el.find('#espionage-name-or-email').val() isnt '' @@ -47,7 +55,7 @@ module.exports = class MainAdminView extends RootView onSearchRequestSuccess: (users) => result = '' if users.length - result = ("<tr><td><code>#{user._id}</code></td><td>#{_.escape(user.name or 'Anoner')}</td><td>#{_.escape(user.email)}</td></tr>" for user in users) + result = ("<tr data-user-id='#{user._id}'><td><code>#{user._id}</code></td><td>#{_.escape(user.name or 'Anoner')}</td><td>#{_.escape(user.email)}</td></tr>" for user in users) result = "<table class=\"table\">#{result.join('\n')}</table>" @$el.find('#user-search-result').html(result) @@ -59,3 +67,25 @@ module.exports = class MainAdminView extends RootView val = $('#increment-field').val() me.set(val, me.get(val) + 1) me.save() + + onClickUserSearchResult: (e) -> + userID = $(e.target).closest('tr').data('user-id') + @openModalView new AdministerUserModal({}, userID) if userID + + onClickFreeSubLink: (e) => + delete @freeSubLink + return unless me.isAdmin() + options = + url: '/db/prepaid/-/create' + data: {type: 'subscription'} + method: 'POST' + options.success = (model, response, options) => + # TODO: Don't hardcode domain. + if application.isProduction() + @freeSubLink = "https://codecombat.com/account/subscription?_ppc=#{model.code}" + else + @freeSubLink = "http://localhost:3000/account/subscription?_ppc=#{model.code}" + @render?() + options.error = (model, response, options) => + console.error 'Failed to create prepaid', response + @supermodel.addRequestResource('create_prepaid', options, 0).load() diff --git a/app/views/admin/PendingPatchesView.coffee b/app/views/admin/PendingPatchesView.coffee new file mode 100644 index 000000000..99a4b2b50 --- /dev/null +++ b/app/views/admin/PendingPatchesView.coffee @@ -0,0 +1,110 @@ +RootView = require 'views/core/RootView' +template = require 'templates/admin/pending-patches-view' +CocoCollection = require 'collections/CocoCollection' +Patch = require 'models/Patch' + +class PendingPatchesCollection extends CocoCollection + url: '/db/patch?view=pending' + model: Patch + +module.exports = class PatchesView extends RootView + id: 'pending-patches-view' + template: template + + constructor: (options) -> + super options + @nameMap = {} + @patches = @supermodel.loadCollection(new PendingPatchesCollection(), 'patches', {cache: false}).model + + onLoaded: -> + super() + @loadUserNames() + @loadAllModelNames() + + getRenderData: -> + c = super() + c.patches = [] + if @supermodel.finished() + comparator = (m) -> m.target.collection + ' ' + m.target.original + patches = _.sortBy (_.clone(patch.attributes) for patch in @patches.models), comparator + c.patches = _.uniq patches, comparator + for patch in c.patches + patch.creatorName = @nameMap[patch.creator] or patch.creator + if name = @nameMap[patch.target.original] + patch.name = name + patch.slug = _.string.slugify name + patch.url = '/editor/' + switch patch.target.collection + when 'level', 'achievement', 'article', 'campaign', 'poll' + "#{patch.target.collection}/#{patch.slug}" + when 'thang_type' + "thang/#{patch.slug}" + when 'level_system', 'level_component' + "level/items?#{patch.target.collection}=#{patch.slug}" + else + console.log "Where do we review a #{patch.target.collection} patch?" + '' + c + + loadUserNames: -> + # Only fetch the names for the userIDs we don't already have in @nameMap + ids = [] + for patch in @patches.models + unless id = patch.get('creator') + console.error 'Found bad user ID in malformed patch', patch + continue + ids.push id unless @nameMap[id] + ids = _.uniq ids + return unless ids.length + + success = (nameMap) => + return if @destroyed + for patch in @patches.models + creatorID = patch.get 'creator' + continue if @nameMap[creatorID] + creator = nameMap[creatorID] + name = creator?.name + name ||= creator.firstName + ' ' + creator.lastName if creator?.firstName + name ||= "Anonymous #{creatorID.substr(18)}" if creator + name ||= '<bad patch data>' + if name.length > 21 + name = name.substr(0, 18) + '...' + @nameMap[creatorID] = name + @render() + + userNamesRequest = @supermodel.addRequestResource 'user_names', { + url: '/db/user/-/names' + data: {ids: ids} + method: 'POST' + success: success + }, 0 + userNamesRequest.load() + + loadAllModelNames: -> + allPatches = (p.attributes for p in @patches.models) + allPatches = _.groupBy allPatches, (p) -> p.target.collection + @loadCollectionModelNames collection, patches for collection, patches of allPatches + + loadCollectionModelNames: (collection, patches) -> + ids = (patch.target.original for patch in patches when not @nameMap[patch.target.original]) + ids = _.uniq ids + return unless ids.length + success = (nameMapArray) => + return if @destroyed + nameMap = {} + for model in nameMapArray + nameMap[model.original or model._id] = model.name + for patch in patches + original = patch.target.original + name = nameMap[original] + if name and name.length > 60 + name = name.substr(0, 57) + '...' + @nameMap[original] = name + @render() + + modelNamesRequest = @supermodel.addRequestResource 'patches', { + url: "/db/#{collection}/names" + data: {ids: ids} + method: 'POST' + success: success + }, 0 + modelNamesRequest.load() diff --git a/app/views/admin/TrialRequestsView.coffee b/app/views/admin/TrialRequestsView.coffee new file mode 100644 index 000000000..5f2f9aec8 --- /dev/null +++ b/app/views/admin/TrialRequestsView.coffee @@ -0,0 +1,66 @@ +RootView = require 'views/core/RootView' +template = require 'templates/admin/trial-requests' +CocoCollection = require 'collections/CocoCollection' +TrialRequest = require 'models/TrialRequest' + +module.exports = class TrialRequestsView extends RootView + id: 'admin-trial-requests-view' + template: template + + events: + 'click .btn-approve': 'onClickApprove' + 'click .btn-deny': 'onClickDeny' + + constructor: (options) -> + super options + if me.isAdmin() + sortRequests = (a, b) -> + statusA = a.get('status') + statusB = b.get('status') + if statusA is 'submitted' and statusB is 'submitted' + if a.get('_id') < b.get('_id') + -1 + else + 1 + else if statusA is 'submitted' + -1 + else if statusB is 'submitted' + 1 + else if not b.get('reviewDate') or a.get('reviewDate') > b.get('reviewDate') + -1 + else + 1 + @trialRequests = new CocoCollection([], { url: '/db/trial.request', model: TrialRequest, comparator: sortRequests }) + @supermodel.loadCollection(@trialRequests, 'trial-requests', {cache: false}) + + getRenderData: -> + context = super() + context.trialRequests = @trialRequests?.models ? [] + context + + onClickApprove: (e) -> + trialRequestID = $(e.target).data('trial-request-id') + trialRequest = _.find @trialRequests.models, (a) -> a.id is trialRequestID + unless trialRequest + console.error 'Could not find trial request model for', trialRequestData + return + trialRequest.set('status', 'approved') + trialRequest.patch + error: (model, response, options) => + console.error 'Error patching trial request', response + success: (model, response, options) => + @render?() + + onClickDeny: (e) -> + trialRequestID = $(e.target).data('trial-request-id') + trialRequest = _.find @trialRequests.models, (a) -> a.id is trialRequestID + unless trialRequest + console.error 'Could not find trial request model for', trialRequestData + return + return unless window.confirm("Deny #{trialRequest.get('properties').email}?") + trialRequest.set('status', 'denied') + trialRequest.patch + error: (model, response, options) => + console.error 'Error patching trial request', response + success: (model, response, options) => + @render?() diff --git a/app/views/admin/UsersView.coffee b/app/views/admin/UsersView.coffee index abd55d4a8..4a4b35906 100644 --- a/app/views/admin/UsersView.coffee +++ b/app/views/admin/UsersView.coffee @@ -17,7 +17,7 @@ module.exports = class UsersView extends RootView # http://mongoosejs.com/docs/queries.html # Each list in conditions is a function call. # The first arg is the function name - # The rest are the agrs for the function + # The rest are the args for the function conditions = [ ['limit', 20] diff --git a/app/views/clans/ClanDetailsView.coffee b/app/views/clans/ClanDetailsView.coffee new file mode 100644 index 000000000..0ffa55e40 --- /dev/null +++ b/app/views/clans/ClanDetailsView.coffee @@ -0,0 +1,352 @@ +RootView = require 'views/core/RootView' +template = require 'templates/clans/clan-details' +app = require 'core/application' +AuthModal = require 'views/core/AuthModal' +CocoCollection = require 'collections/CocoCollection' +Campaign = require 'models/Campaign' +Clan = require 'models/Clan' +EarnedAchievement = require 'models/EarnedAchievement' +LevelSession = require 'models/LevelSession' +SubscribeModal = require 'views/core/SubscribeModal' +ThangType = require 'models/ThangType' +User = require 'models/User' + +# TODO: Add message for clan not found +# TODO: Progress visual for premium levels? +# TODO: Add expanded level names toggle +# TODO: Only need campaign data if clan is private + +module.exports = class ClanDetailsView extends RootView + id: 'clan-details-view' + template: template + + events: + 'change .expand-progress-checkbox': 'onExpandedProgressCheckbox' + 'click .delete-clan-btn': 'onDeleteClan' + 'click .edit-description-save-btn': 'onEditDescriptionSave' + 'click .edit-name-save-btn': 'onEditNameSave' + 'click .join-clan-btn': 'onJoinClan' + 'click .leave-clan-btn': 'onLeaveClan' + 'click .member-header': 'onClickMemberHeader' + 'click .progress-header': 'onClickProgressHeader' + 'click .progress-level-cell': 'onClickLevel' + 'click .remove-member-btn': 'onRemoveMember' + 'mouseenter .progress-level-cell': 'onMouseEnterPoint' + 'mouseleave .progress-level-cell': 'onMouseLeavePoint' + + constructor: (options, @clanID) -> + super options + @initData() + + destroy: -> + @stopListening?() + + initData: -> + @showExpandedProgress = false + @memberSort = 'nameAsc' + @stats = {} + + @campaigns = new CocoCollection([], { url: "/db/campaign", model: Campaign, comparator:'_id' }) + @clan = new Clan _id: @clanID + @members = new CocoCollection([], { url: "/db/clan/#{@clanID}/members", model: User, comparator: 'nameLower' }) + @memberAchievements = new CocoCollection([], { url: "/db/clan/#{@clanID}/member_achievements", model: EarnedAchievement, comparator:'_id' }) + # MemberSessions: only loads creatorName, levelName, codeLanguage, submittedCodeLanguage for each session + @memberSessions = new CocoCollection([], { url: "/db/clan/#{@clanID}/member_sessions", model: LevelSession, comparator:'_id' }) + + @listenTo me, 'sync', => @render?() + @listenTo @campaigns, 'sync', @onCampaignSync + @listenTo @clan, 'sync', @onClanSync + @listenTo @members, 'sync', @onMembersSync + @listenTo @memberAchievements, 'sync', @onMemberAchievementsSync + @listenTo @memberSessions, 'sync', @onMemberSessionsSync + + @supermodel.loadModel @campaigns, 'clan', cache: false + @supermodel.loadModel @clan, 'clan', cache: false + @supermodel.loadCollection(@members, 'members', {cache: false}) + @supermodel.loadCollection(@memberAchievements, 'member_achievements', {cache: false}) + @supermodel.loadCollection(@memberSessions, 'member_sessions', {cache: false}) + + getRenderData: -> + context = super() + context.campaignLevelProgressions = @campaignLevelProgressions ? [] + context.clan = @clan + context.conceptsProgression = @conceptsProgression ? [] + if application.isProduction() + context.joinClanLink = "https://codecombat.com/clans/#{@clanID}" + else + context.joinClanLink = "http://localhost:3000/clans/#{@clanID}" + context.owner = @owner + context.memberAchievementsMap = @memberAchievementsMap + context.memberLanguageMap = @memberLanguageMap + context.memberLevelStateMap = @memberLevelMap ? {} + context.memberMaxLevelCount = @memberMaxLevelCount + context.memberSort = @memberSort + context.isOwner = @clan.get('ownerID') is me.id + context.isMember = @clanID in (me.get('clans') ? []) + context.stats = @stats + + # Find last campaign level for each user + # TODO: why do we do this for every render? + highestUserLevelCountMap = {} + lastUserCampaignLevelMap = {} + maxLastUserCampaignLevel = 0 + userConceptsMap = {} + if @campaigns.loaded + levelCount = 0 + for campaign in @campaigns.models + campaignID = campaign.id + lastLevelIndex = 0 + for levelID, level of campaign.get('levels') + levelSlug = level.slug + for member in @members?.models ? [] + if context.memberLevelStateMap[member.id]?[levelSlug] + lastUserCampaignLevelMap[member.id] ?= {} + lastUserCampaignLevelMap[member.id][campaignID] ?= {} + lastUserCampaignLevelMap[member.id][campaignID] = + levelSlug: levelSlug + index: lastLevelIndex + maxLastUserCampaignLevel = lastLevelIndex if lastLevelIndex > maxLastUserCampaignLevel + if level.concepts? + userConceptsMap[member.id] ?= {} + for concept in level.concepts + continue if userConceptsMap[member.id][concept] is 'complete' + userConceptsMap[member.id][concept] = context.memberLevelStateMap[member.id][levelSlug].state + highestUserLevelCountMap[member.id] = levelCount + lastLevelIndex++ + levelCount++ + + @sortMembers(highestUserLevelCountMap, userConceptsMap) if @clan.get('dashboardType') is 'premium' + context.members = @members?.models ? [] + context.lastUserCampaignLevelMap = lastUserCampaignLevelMap + context.showExpandedProgress = maxLastUserCampaignLevel <= 30 or @showExpandedProgress + context.userConceptsMap = userConceptsMap + context + + afterRender: -> + super() + @updateHeroIcons() + + refreshData: -> + me.fetch cache: false + @members.fetch cache: false + @memberAchievements.fetch cache: false + @memberSessions.fetch cache: false + + sortMembers: (highestUserLevelCountMap, userConceptsMap) -> + # Progress sort precedence: most completed concepts, most started concepts, most levels, name sort + return unless @members? and @memberSort? + switch @memberSort + when "nameDesc" + @members.comparator = (a, b) -> return (b.get('name') or 'Anoner').localeCompare(a.get('name') or 'Anoner') + when "progressAsc" + @members.comparator = (a, b) -> + aComplete = (concept for concept, state of userConceptsMap[a.id] when state is 'complete') + bComplete = (concept for concept, state of userConceptsMap[b.id] when state is 'complete') + aStarted = (concept for concept, state of userConceptsMap[a.id] when state is 'started') + bStarted = (concept for concept, state of userConceptsMap[b.id] when state is 'started') + if aComplete < bComplete then return -1 + else if aComplete > bComplete then return 1 + else if aStarted < bStarted then return -1 + else if aStarted > bStarted then return 1 + if highestUserLevelCountMap[a.id] < highestUserLevelCountMap[b.id] then return -1 + else if highestUserLevelCountMap[a.id] > highestUserLevelCountMap[b.id] then return 1 + (a.get('name') or 'Anoner').localeCompare(b.get('name') or 'Anoner') + when "progressDesc" + @members.comparator = (a, b) -> + aComplete = (concept for concept, state of userConceptsMap[a.id] when state is 'complete') + bComplete = (concept for concept, state of userConceptsMap[b.id] when state is 'complete') + aStarted = (concept for concept, state of userConceptsMap[a.id] when state is 'started') + bStarted = (concept for concept, state of userConceptsMap[b.id] when state is 'started') + if aComplete > bComplete then return -1 + else if aComplete < bComplete then return 1 + else if aStarted > bStarted then return -1 + else if aStarted < bStarted then return 1 + if highestUserLevelCountMap[a.id] > highestUserLevelCountMap[b.id] then return -1 + else if highestUserLevelCountMap[a.id] < highestUserLevelCountMap[b.id] then return 1 + (b.get('name') or 'Anoner').localeCompare(a.get('name') or 'Anoner') + else + @members.comparator = (a, b) -> return (a.get('name') or 'Anoner').localeCompare(b.get('name') or 'Anoner') + @members.sort() + + updateHeroIcons: -> + return unless @members?.models? + for member in @members.models + continue unless hero = member.get('heroConfig')?.thangType + for slug, original of ThangType.heroes when original is hero + @$el.find(".player-hero-icon[data-memberID=#{member.id}]").removeClass('.player-hero-icon').addClass('player-hero-icon ' + slug) + + onCampaignSync: -> + return unless @campaigns.loaded + @campaignLevelProgressions = [] + @conceptsProgression = [] + for campaign in @campaigns.models + continue if campaign.get('slug') is 'auditions' + campaignLevelProgression = + ID: campaign.id + slug: campaign.get('slug') + name: campaign.get('fullName') or campaign.get('name') + levels: [] + for levelID, level of campaign.get('levels') + campaignLevelProgression.levels.push + ID: levelID + slug: level.slug + name: level.name + if level.concepts? + for concept in level.concepts + @conceptsProgression.push concept unless concept in @conceptsProgression + @campaignLevelProgressions.push campaignLevelProgression + @render?() + + onClanSync: -> + unless @owner? + @owner = new User _id: @clan.get('ownerID') + @listenTo @owner, 'sync', => @render?() + @supermodel.loadModel @owner, 'owner', cache: false + @render?() + + onMembersSync: -> + @stats.averageLevel = Math.round(@members.reduce(((sum, member) -> sum + member.level()), 0) / @members.length) + @render?() + + onMemberAchievementsSync: -> + @memberAchievementsMap = {} + for achievement in @memberAchievements.models + user = achievement.get('user') + @memberAchievementsMap[user] ?= [] + @memberAchievementsMap[user].push achievement + for user of @memberAchievementsMap + @memberAchievementsMap[user].sort (a, b) -> b.id.localeCompare(a.id) + @stats.averageAchievements = Math.round(@memberAchievements.models.length / Object.keys(@memberAchievementsMap).length) + @render?() + + onMemberSessionsSync: -> + @memberLevelMap = {} + memberSessions = {} + for levelSession in @memberSessions.models + continue if levelSession.isMultiplayer() + user = levelSession.get('creator') + levelSlug = levelSession.get('levelID') + @memberLevelMap[user] ?= {} + @memberLevelMap[user][levelSlug] ?= {} + levelInfo = + level: levelSession.get('levelName') + levelID: levelSession.get('levelID') + changed: new Date(levelSession.get('changed')).toLocaleString() + playtime: levelSession.get('playtime') + sessionID: levelSession.id + @memberLevelMap[user][levelSlug].levelInfo = levelInfo + if levelSession.get('state')?.complete is true + @memberLevelMap[user][levelSlug].state = 'complete' + memberSessions[user] ?= [] + memberSessions[user].push levelSession + else + @memberLevelMap[user][levelSlug].state = 'started' + @memberMaxLevelCount = 0 + @memberLanguageMap = {} + for user of memberSessions + languageCounts = {} + for levelSession in memberSessions[user] + language = levelSession.get('codeLanguage') or levelSession.get('submittedCodeLanguage') + languageCounts[language] = (languageCounts[language] or 0) + 1 if language + @memberMaxLevelCount = memberSessions[user].length if @memberMaxLevelCount < memberSessions[user].length + mostUsedCount = 0 + for language, count of languageCounts + if count > mostUsedCount + mostUsedCount = count + @memberLanguageMap[user] = language + @render?() + + onMouseEnterPoint: (e) -> + $('.level-popup-container').hide() + container = $(e.target).find('.level-popup-container').show() + margin = 20 + offset = $(e.target).offset() + scrollTop = $(e.target).offsetParent().scrollTop() + height = container.outerHeight() + container.css('left', offset.left + e.offsetX) + container.css('top', offset.top + scrollTop - height - margin) + + onMouseLeavePoint: (e) -> + $(e.target).find('.level-popup-container').hide() + + onClickLevel: (e) -> + levelInfo = $(e.target).data 'level-info' + return unless levelInfo?.levelID? and levelInfo?.sessionID? + url = "/play/level/#{levelInfo.levelID}?session=#{levelInfo.sessionID}&observing=true" + window.open url, '_blank' + + onDeleteClan: (e) -> + return @openModalView(new AuthModal()) if me.isAnonymous() + return unless window.confirm("Delete Clan?") + options = + url: "/db/clan/#{@clanID}" + method: 'DELETE' + error: (model, response, options) => + console.error 'Error joining clan', response + success: (model, response, options) => + app.router.navigate "/clans" + window.location.reload() + @supermodel.addRequestResource( 'delete_clan', options).load() + + onEditDescriptionSave: (e) -> + description = $('.edit-description-input').val() + @clan.set 'description', description + @clan.patch() + $('#editDescriptionModal').modal('hide') + + onEditNameSave: (e) -> + if name = $('.edit-name-input').val() + @clan.set 'name', name + @clan.patch() + $('#editNameModal').modal('hide') + + onExpandedProgressCheckbox: (e) -> + @showExpandedProgress = $('.expand-progress-checkbox').prop('checked') + # TODO: why does render reset the checkbox to be unchecked? + @render?() + $('.expand-progress-checkbox').attr('checked', @showExpandedProgress) + + onJoinClan: (e) -> + return @openModalView(new AuthModal()) if me.isAnonymous() + return unless @clan.loaded + if @clan.get('type') is 'private' and not me.isPremium() + @openModalView new SubscribeModal() + window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'join clan' + return + options = + url: "/db/clan/#{@clanID}/join" + method: 'PUT' + error: (model, response, options) => + console.error 'Error joining clan', response + success: (model, response, options) => @refreshData() + @supermodel.addRequestResource( 'join_clan', options).load() + + onLeaveClan: (e) -> + options = + url: "/db/clan/#{@clanID}/leave" + method: 'PUT' + error: (model, response, options) => + console.error 'Error leaving clan', response + success: (model, response, options) => @refreshData() + @supermodel.addRequestResource( 'leave_clan', options).load() + + onClickMemberHeader: (e) -> + @memberSort = if @memberSort is 'nameAsc' then 'nameDesc' else 'nameAsc' + @render?() + + onClickProgressHeader: (e) -> + @memberSort = if @memberSort is 'progressAsc' then 'progressDesc' else 'progressAsc' + @render?() + + onRemoveMember: (e) -> + return unless window.confirm("Remove Hero?") + if memberID = $(e.target).data('id') + options = + url: "/db/clan/#{@clanID}/remove/#{memberID}" + method: 'PUT' + error: (model, response, options) => + console.error 'Error removing clan member', response + success: (model, response, options) => @refreshData() + @supermodel.addRequestResource( 'remove_member', options).load() + else + console.error "No member ID attached to remove button." diff --git a/app/views/clans/ClansView.coffee b/app/views/clans/ClansView.coffee new file mode 100644 index 000000000..50c3d806d --- /dev/null +++ b/app/views/clans/ClansView.coffee @@ -0,0 +1,151 @@ +app = require 'core/application' +AuthModal = require 'views/core/AuthModal' +RootView = require 'views/core/RootView' +template = require 'templates/clans/clans' +CocoCollection = require 'collections/CocoCollection' +Clan = require 'models/Clan' +SubscribeModal = require 'views/core/SubscribeModal' + +# TODO: Waiting for async messages +# TODO: Invalid clan name message +# TODO: Refresh data instead of page + +module.exports = class ClansView extends RootView + id: 'clans-view' + template: template + + events: + 'click .create-clan-btn': 'onClickCreateClan' + 'click .join-clan-btn': 'onJoinClan' + 'click .leave-clan-btn': 'onLeaveClan' + 'click .private-clan-checkbox': 'onClickPrivateCheckbox' + + constructor: (options) -> + super options + @initData() + + destroy: -> + @stopListening?() + + getRenderData: -> + context = super() + context.idNameMap = @idNameMap + context.publicClans = _.filter(@publicClans.models, (clan) -> clan.get('type') is 'public') + context.myClans = @myClans.models + context.myClanIDs = me.get('clans') ? [] + context + + afterRender: -> + super() + @setupPrivateInfoPopover() + + initData: -> + @idNameMap = {} + + sortClanList = (a, b) -> + if a.get('members').length isnt b.get('members').length + if a.get('members').length < b.get('members').length then 1 else -1 + else + b.id.localeCompare(a.id) + @publicClans = new CocoCollection([], { url: '/db/clan/-/public', model: Clan, comparator: sortClanList }) + @listenTo @publicClans, 'sync', => + @refreshNames @publicClans.models + @render?() + @supermodel.loadCollection(@publicClans, 'public_clans', {cache: false}) + @myClans = new CocoCollection([], { url: "/db/user/#{me.id}/clans", model: Clan, comparator: sortClanList }) + @listenTo @myClans, 'sync', => + @refreshNames @myClans.models + @render?() + @supermodel.loadCollection(@myClans, 'my_clans', {cache: false}) + @listenTo me, 'sync', => @render?() + + refreshNames: (clans) -> + clanIDs = _.filter(clans, (clan) -> clan.get('type') is 'public') + clanIDs = _.map(clans, (clan) -> clan.get('ownerID')) + options = + url: '/db/user/-/names' + method: 'POST' + data: {ids: clanIDs} + success: (models, response, options) => + @idNameMap[userID] = models[userID].name for userID of models + @render?() + @supermodel.addRequestResource('user_names', options, 0).load() + + setupPrivateInfoPopover: -> + popoverTitle = "<h3>Private Clans</h3>" + popoverContent = "<ul>" + popoverContent += "<li><span style='font-weight:bold;'>Track concepts</span> learned by each member" + popoverContent += "<li>Track levels completed for each member" + popoverContent += "<li>See your members' <span style='font-weight:bold;'>solutions</span>" + popoverContent += "<li>Sort members by name or progress" + popoverContent += "<li><span style='font-weight:bold;'>Requires invitation</span> to join" + popoverContent += "</ul>" + popoverContent += "<p><img src='/images/pages/clans/dashboard_preview.png' height='400'></p>" + popoverContent += "<p>Private clans require a subscription to create or join.</p>" + @$el.find('.private-more-info').popover( + animation: true + html: true + placement: 'right' + trigger: 'hover' + title: popoverTitle + content: popoverContent + container: @$el + ) + + onClickCreateClan: (e) -> + return @openModalView new AuthModal() if me.isAnonymous() + clanType = if $('.private-clan-checkbox').prop('checked') then 'private' else 'public' + if clanType is 'private' and not me.isPremium() + @openModalView new SubscribeModal() + window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'create clan' + return + if name = $('.create-clan-name').val() + clan = new Clan() + clan.set 'type', clanType + clan.set 'name', name + clan.set 'description', description if description = $('.create-clan-description').val() + clan.save {}, + error: (model, response, options) => + console.error 'Error saving clan', response.status + success: (model, response, options) => + app.router.navigate "/clans/#{model.id}" + window.location.reload() + else + console.log 'Invalid name' + + onJoinClan: (e) -> + return @openModalView(new AuthModal()) if me.isAnonymous() + if clanID = $(e.target).data('id') + options = + url: "/db/clan/#{clanID}/join" + method: 'PUT' + error: (model, response, options) => + console.error 'Error joining clan', response + success: (model, response, options) => + app.router.navigate "/clans/#{clanID}" + window.location.reload() + @supermodel.addRequestResource( 'join_clan', options).load() + else + console.error "No clan ID attached to join button." + + onLeaveClan: (e) -> + if clanID = $(e.target).data('id') + options = + url: "/db/clan/#{clanID}/leave" + method: 'PUT' + error: (model, response, options) => + console.error 'Error leaving clan', response + success: (model, response, options) => + me.fetch cache: false + @publicClans.fetch cache: false + @myClans.fetch cache: false + @supermodel.addRequestResource( 'leave_clan', options).load() + else + console.error "No clan ID attached to leave button." + + onClickPrivateCheckbox: (e) -> + return @openModalView new AuthModal() if me.isAnonymous() + if $('.private-clan-checkbox').prop('checked') and not me.isPremium() + $('.private-clan-checkbox').attr('checked', false) + @openModalView new SubscribeModal() + window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'check private clan' diff --git a/app/views/common/SearchView.coffee b/app/views/common/SearchView.coffee index cf8ffbbeb..19e6c2ad2 100644 --- a/app/views/common/SearchView.coffee +++ b/app/views/common/SearchView.coffee @@ -1,16 +1,16 @@ RootView = require 'views/core/RootView' -NewModelModal = require 'views/modal/NewModelModal' -template = require 'templates/common/search' +NewModelModal = require 'views/editor/modal/NewModelModal' +template = require 'templates/common/search-view' app = require 'core/application' class SearchCollection extends Backbone.Collection initialize: (modelURL, @model, @term, @projection) -> @url = "#{modelURL}?project=" - if @projection? and not (@projection == []) + if @projection?.length @url += 'created,permissions' - @url += ',' + projected for projected in projection + @url += ',' + projected for projected in @projection else @url += 'true' - @url += "&term=#{term}" if @term + @url += "&term=#{@term}" if @term comparator: (a, b) -> score = 0 @@ -77,7 +77,7 @@ module.exports = class SearchView extends RootView @hideLoading() @collection.sort() documents = @collection.models - table = $(@tableTemplate(documents: documents, me: me, page: @page)) + table = $(@tableTemplate(documents: documents, me: me, page: @page, moment: moment)) @$el.find('table').replaceWith(table) @$el.find('table').i18n() diff --git a/app/views/common/UserView.coffee b/app/views/common/UserView.coffee index 480dfed6c..2c79f5261 100644 --- a/app/views/common/UserView.coffee +++ b/app/views/common/UserView.coffee @@ -17,7 +17,7 @@ module.exports = class UserView extends RootView @user = me @onLoaded() @user = new User _id: @userID - @supermodel.loadModel @user, 'user' + @supermodel.loadModel @user, 'user', cache: false getRenderData: -> context = super() diff --git a/app/views/contribute/ContributeClassView.coffee b/app/views/contribute/ContributeClassView.coffee index 18be62776..8bead529b 100644 --- a/app/views/contribute/ContributeClassView.coffee +++ b/app/views/contribute/ContributeClassView.coffee @@ -1,4 +1,4 @@ -SignupModalView = require 'views/modal/SignupModal' +AuthModal = require 'views/core/AuthModal' RootView = require 'views/core/RootView' {me} = require 'core/auth' contributorSignupAnonymousTemplate = require 'templates/contribute/contributor_signup_anonymous' @@ -37,7 +37,7 @@ module.exports = class ContributeClassView extends RootView me.setEmailSubscription subscription+'News', checked me.patch() - @openModalView new SignupModalView() if me.get 'anonymous' + @openModalView new AuthModal() if me.get 'anonymous' el.parent().find('.saved-notification').finish().show('fast').delay(3000).fadeOut(2000) contributors: [] diff --git a/app/views/contribute/DiplomatView.coffee b/app/views/contribute/DiplomatView.coffee index 5756274cb..a27472cf3 100644 --- a/app/views/contribute/DiplomatView.coffee +++ b/app/views/contribute/DiplomatView.coffee @@ -2,6 +2,60 @@ ContributeClassView = require './ContributeClassView' template = require 'templates/contribute/diplomat' {me} = require 'core/auth' +require("locale/en") +require("locale/en-US") +require("locale/en-GB") +require("locale/ru") +require("locale/de-DE") +require("locale/de-AT") +require("locale/de-CH") +require("locale/es-419") +require("locale/es-ES") +require("locale/zh-HANS") +require("locale/zh-HANT") +require("locale/zh-WUU-HANS") +require("locale/zh-WUU-HANT") +require("locale/fr") +require("locale/ja") +require("locale/ar") +require("locale/pt-BR") +require("locale/pt-PT") +require("locale/pl") +require("locale/it") +require("locale/tr") +require("locale/nl-BE") +require("locale/nl-NL") +require("locale/fa") +require("locale/cs") +require("locale/sv") +require("locale/id") +require("locale/el") +require("locale/ro") +require("locale/vi") +require("locale/hu") +require("locale/th") +require("locale/da") +require("locale/ko") +require("locale/sk") +require("locale/sl") +require("locale/fi") +require("locale/bg") +require("locale/nb") +require("locale/nn") +require("locale/he") +require("locale/lt") +require("locale/sr") +require("locale/uk") +require("locale/hi") +require("locale/ur") +require("locale/ms") +require("locale/ca") +require("locale/gl") +require("locale/mk-MK") +require("locale/eo") +require("locale/uz") +require("locale/my") + module.exports = class DiplomatView extends ContributeClassView id: 'diplomat-view' template: template @@ -19,6 +73,7 @@ module.exports = class DiplomatView extends ContributeClassView totalStrings = @countStrings @locale.en languageStats = {} for languageCode, language of @locale + continue if languageCode is 'update' languageStats[languageCode] = githubURL: "https://github.com/codecombat/codecombat/blob/master/app/locale/#{languageCode}.coffee" completion: @countStrings(language) / totalStrings @@ -38,51 +93,53 @@ module.exports = class DiplomatView extends ContributeClassView en: [] # English - English 'en-US': [] # English (US), English (US) 'en-GB': [] # English (UK), English (UK) - 'en-AU': [] # English (AU), English (AU) - ru: ['fess89', 'ser-storchak', 'Mr A', 'a1ip', 'iulianR', 'EagleTA', 'kisik21', 'Shpionus', 'kerradus', 'ImmortalJoker'] # русский язык, Russian - 'de-DE': ['Dirk', 'faabsen', 'HiroP0', 'Anon', 'bkimminich', 'bahuma20', 'domenukk', 'dkundel', 'djsmith85'] # Deutsch (Deutschland), German (Germany) + ru: ['EagleTA', 'ImmortalJoker', 'Mr A', 'Shpionus', 'a1ip', 'fess89', 'iulianR', 'kerradus', 'kisik21', 'nixel', 'ser-storchak'] # русский язык, Russian + 'de-DE': ['Anon', 'Dirk', 'HiroP0', 'bahuma20', 'bkimminich', 'djsmith85', 'dkundel', 'domenukk', 'faabsen'] # Deutsch (Deutschland), German (Germany) 'de-AT': ['djsmith85'] # Deutsch (Österreich), German (Austria) 'de-CH': ['greyhusky'] # Deutsch (Schweiz), German (Switzerland) - 'es-419': ['Jesús Ruppel', 'Matthew Burt', 'Mariano Luzza', '2xG', ] # español (América Latina), Spanish (Latin America) - 'es-ES': ['Matthew Burt', 'DanielRodriguezRivero', 'Anon', 'Pouyio', '3rr3s3v3n', 'OviiiOne', 'Vindurrin'] # español (ES), Spanish (Spain) - 'zh-HANS': ['Adam23', 'spacepope', 'yangxuan8282', 'Cheng Zheng', 'yfdyh000', 'julycoolwind', 'Vic020', 'onion7878', 'BonnieBBS', '1c7', 'ZephyrSails'] # 简体中文, Chinese (Simplified) - 'zh-HANT': ['gintau', 'Adam23'] # 繁体中文, Chinese (Traditional) + 'es-419': ['2xG', 'Federico Tomas', 'Jesús Ruppel', 'Mariano Luzza', 'Matthew Burt'] # español (América Latina), Spanish (Latin America) + 'es-ES': ['3rr3s3v3n', 'Anon', 'DanielRodriguezRivero', 'Matthew Burt', 'OviiiOne', 'Pouyio', 'Vindurrin'] # español (ES), Spanish (Spain) + 'zh-HANS': ['1c7', 'Adam23', 'BonnieBBS', 'Cheng Zheng', 'Vic020', 'ZephyrSails', 'julycoolwind', 'onion7878', 'spacepope', 'yangxuan8282', 'yfdyh000'] # 简体中文, Chinese (Simplified) + 'zh-HANT': ['Adam23', 'gintau'] # 繁体中文, Chinese (Traditional) 'zh-WUU-HANS': [] # 吴语, Wuu (Simplified) 'zh-WUU-HANT': ['benojan'] # 吳語, Wuu (Traditional) - fr: ['Xeonarno', 'Elfisen', 'Armaldio', 'MartinDelille', 'pstweb', 'veritable', 'jaybi', 'xavismeh', 'Anon', 'Feugy', 'dc55028', 'ChrisLightman', 'Oaugereau'] # français, French - ja: ['g1itch', 'kengos', 'treby'] # 日本語, Japanese - ar: ['ahmed80dz', '5y'] # العربية, Arabic - 'pt-BR': ['Gutenberg Barros', 'Kieizroe', 'Matthew Burt', 'brunoporto', 'cassiocardoso', 'Bia41'] # português do Brasil, Portuguese (Brazil) - 'pt-PT': ['Matthew Burt', 'ReiDuKuduro', 'Imperadeiro98', 'batista', 'ProgramadorLucas', 'gutierri'] # Português (Portugal), Portuguese (Portugal) + fr: ['Anon', 'Armaldio', 'ChrisLightman', 'Elfisen', 'Feugy', 'MartinDelille', 'Oaugereau', 'Xeonarno', 'dc55028', 'jaybi', 'pstweb', 'veritable', 'xavismeh'] # français, French + ja: ['Coderaulic', 'g1itch', 'kengos', 'treby'] # 日本語, Japanese + ar: ['5y', 'ahmed80dz'] # العربية, Arabic + 'pt-BR': ['Bia41', 'Gutenberg Barros', 'Kieizroe', 'Matthew Burt', 'brunoporto', 'cassiocardoso', 'jklemm'] # português do Brasil, Portuguese (Brazil) + 'pt-PT': ['Imperadeiro98', 'Matthew Burt', 'ProgramadorLucas', 'ReiDuKuduro', 'batista', 'gutierri'] # Português (Portugal), Portuguese (Portugal) pl: ['Anon', 'Kacper Ciepielewski', 'TigroTigro', 'kvasnyk'] # język polski, Polish - it: ['flauta', 'AlessioPaternoster'] # italiano, Italian - tr: ['Nazım Gediz Aydındoğmuş', 'cobaimelan', 'wakeup', 'gediz', 'ilisyus'] # Türkçe, Turkish + it: ['AlessioPaternoster', 'flauta', 'Atomk'] # italiano, Italian + tr: ['Nazım Gediz Aydındoğmuş', 'cobaimelan', 'gediz', 'ilisyus', 'wakeup'] # Türkçe, Turkish 'nl-BE': ['Glen De Cauwsemaecker', 'Ruben Vereecken'] # Nederlands (België), Dutch (Belgium) - 'nl-NL': ['Jasper D\'haene', 'Guido Zuidhof'] # Nederlands (Nederland), Dutch (Netherlands) + 'nl-NL': ['Guido Zuidhof', "Jasper D\'haene"] # Nederlands (Nederland), Dutch (Netherlands) fa: ['Reza Habibi (Rehb)'] # فارسی, Persian - cs: ['vanous'] # čeština, Czech - sv: ['iamhj'] # Svenska, Swedish + cs: ['Martin005', 'Gygram', 'vanous'] # čeština, Czech + sv: ['iamhj', 'Galaky'] # Svenska, Swedish id: ['mlewisno-oberlin'] # Bahasa Indonesia, Indonesian el: ['Stergios'] # ελληνικά, Greek ro: [] # limba română, Romanian vi: ['An Nguyen Hoang Thien'] # Tiếng Việt, Vietnamese - hu: ['ferpeter', 'csuvsaregal', 'atlantisguru', 'Anon', 'kinez', 'bbeasmile'] # magyar, Hungarian + hu: ['Anon', 'atlantisguru', 'bbeasmile', 'csuvsaregal', 'divaDseidnA', 'ferpeter', 'kinez'] # magyar, Hungarian th: ['Kamolchanok Jittrepit'] # ไทย, Thai - da: ['Einar Rasmussen', 'sorsjen', 'Randi Hillerøe', 'Anon', 'Silwing', 'Rahazan', 'marc-portier'] # dansk, Danish + da: ['Anon', 'Einar Rasmussen', 'Rahazan', 'Randi Hillerøe', 'Silwing', 'marc-portier', 'sorsjen', 'Zleep-Dogg'] # dansk, Danish ko: ['Melondonut'] # 한국어, Korean - sk: ['Anon'] # slovenčina, Slovak + sk: ['Anon', 'Juraj Pecháč'] # slovenčina, Slovak sl: [] # slovenščina, Slovene fi: [] # suomi, Finnish bg: [] # български език, Bulgarian - no: ['bardeh', 'torehaug'] # Norsk, Norwegian - nn: [] # Norwegian (Nynorsk), Norwegian Nynorsk - nb: ['ebirkenes','mcclane654'] # Norsk Bokmål, Norwegian (Bokmål) + nb: ['bardeh', 'ebirkenes', 'matifol', 'mcclane654', 'mogsie', 'torehaug'] # Norsk Bokmål, Norwegian (Bokmål) + nn: [] # Norsk Nynorsk, Norwegian (Nynorsk) he: ['OverProgram', 'monetita'] # עברית, Hebrew lt: [] # lietuvių kalba, Lithuanian sr: [] # српски, Serbian - uk: ['fess89', 'ImmortalJoker', 'gorodsb', 'endrilian', 'OlenaGapak'] # українська мова, Ukrainian + uk: ['ImmortalJoker', 'OlenaGapak', 'Rarst', 'endrilian', 'fess89', 'gorodsb', 'probil'] # українська мова, Ukrainian hi: [] # मानक हिन्दी, Hindi ur: [] # اُردُو, Urdu ms: [] # Bahasa Melayu, Bahasa Malaysia ca: ['ArniMcFrag'] # Català, Catalan gl: ['mcaeiror'] # Galego, Galician + 'mk-MK': ['SuperPranx'] # Македонски, Macedonian + eo: [] # Esperanto, Esperanto + uz: [] # O'zbekcha, Uzbek + my: [] # မြန်မာစကား, Myanmar language diff --git a/app/views/core/AchievementPopup.coffee b/app/views/core/AchievementPopup.coffee index fa7367af8..4d0b2036d 100644 --- a/app/views/core/AchievementPopup.coffee +++ b/app/views/core/AchievementPopup.coffee @@ -1,5 +1,5 @@ CocoView = require 'views/core/CocoView' -template = require 'templates/achievements/achievement-popup' +template = require 'templates/core/achievement-popup' User = require '../../models/User' Achievement = require '../../models/Achievement' @@ -85,4 +85,4 @@ module.exports = class AchievementPopup extends CocoView _.delay @initializeTooltips, 1000 # TODO this could be smoother initializeTooltips: -> - $('.progress-bar').tooltip() + $('.progress-bar').addClass('has-tooltip').tooltip() diff --git a/app/views/core/AuthModal.coffee b/app/views/core/AuthModal.coffee index adde6422a..7a5845bdf 100644 --- a/app/views/core/AuthModal.coffee +++ b/app/views/core/AuthModal.coffee @@ -14,11 +14,11 @@ module.exports = class AuthModal extends ModalView # login buttons 'click #switch-to-signup-button': 'onSignupInstead' 'click #switch-to-login-button': 'onLoginInstead' - 'click #confirm-age': 'checkAge' 'click #github-login-button': 'onGitHubLoginClicked' 'submit': 'onSubmitForm' # handles both submit buttons 'keyup #name': 'onNameChange' 'click #gplus-login-button': 'onClickGPlusLogin' + 'click #close-modal': 'hide' subscriptions: 'errors:server-error': 'onServerError' @@ -33,17 +33,16 @@ module.exports = class AuthModal extends ModalView getRenderData: -> c = super() c.showRequiredError = @options.showRequiredError - c.title = {0: 'short', 1: 'long'}[me.get('testGroupNumber') % 2] - c.descriptionOn = {0: 'yes', 1: 'no'}[Math.floor(me.get('testGroupNumber')/2) % 2] - if @mode is 'signup' - application.tracker.identify authModalTitle: c.title - application.tracker.trackEvent 'Started Signup', authModalTitle: c.title, descriptionOn: c.descriptionOn + c.showSignupRationale = @options.showSignupRationale c.mode = @mode c.formValues = @previousFormInputs or {} - c.onEmployersPage = Backbone.history.fragment is "employers" c.me = me c + afterRender: -> + super() + @$el.toggleClass('signup', @mode is 'signup').toggleClass('login', @mode is 'login') + afterInsert: -> super() _.delay (=> application.router.renderLoginButtons()), 500 @@ -69,23 +68,27 @@ module.exports = class AuthModal extends ModalView if @mode is 'login' then @loginAccount() else @createAccount() false - checkAge: (e) -> - @playSound 'menu-button-click' - $('#signup-button', @$el).prop 'disabled', not $(e.target).prop('checked') - loginAccount: -> forms.clearFormAlerts(@$el) userObject = forms.formToObject @$el res = tv4.validateMultiple userObject, User.schema return forms.applyErrorsToForm(@$el, res.errors) unless res.valid @enableModalInProgress(@$el) # TODO: part of forms - loginUser(userObject) + loginUser userObject, null, window.nextURL + + emailCheck: -> + email = $('#email', @$el).val() + filter = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i # https://news.ycombinator.com/item?id=5763990 + unless filter.test(email) + forms.setErrorToProperty @$el, 'email', 'Please enter a valid email address', true + return false + return true createAccount: -> forms.clearFormAlerts(@$el) + return unless @emailCheck() userObject = forms.formToObject @$el delete userObject.subscribe - delete userObject['confirm-age'] delete userObject.name if userObject.name is '' userObject.name = @suggestedName if @suggestedName for key, val of me.attributes when key in ['preferredLanguage', 'testGroupNumber', 'dateCreated', 'wizardColor1', 'name', 'music', 'volume', 'emails'] @@ -97,9 +100,9 @@ module.exports = class AuthModal extends ModalView res = tv4.validateMultiple userObject, User.schema return forms.applyErrorsToForm(@$el, res.errors) unless res.valid Backbone.Mediator.publish "auth:signed-up", {} - window.tracker?.trackEvent 'Finished Signup' + window.tracker?.trackEvent 'Finished Signup', label: 'CodeCombat' @enableModalInProgress(@$el) - createUser userObject, null, window.nextLevelURL + createUser userObject, null, window.nextURL onLoggingInWithFacebook: (e) -> modal = $('.modal:visible', @$el) @@ -126,7 +129,6 @@ module.exports = class AuthModal extends ModalView gplusAuthSteps: [ { i18n: 'login.authenticate_gplus', done: false } { i18n: 'login.load_profile', done: false } - { i18n: 'login.load_email', done: false } { i18n: 'login.finishing', done: false } ] @@ -143,16 +145,12 @@ module.exports = class AuthModal extends ModalView @gplusAuthSteps[1].done = true @renderGPlusAuthChecklist() - @listenToOnce handler, 'email-loaded', -> + @listenToOnce handler, 'logging-into-codecombat', -> @gplusAuthSteps[2].done = true @renderGPlusAuthChecklist() - @listenToOnce handler, 'logging-into-codecombat', -> - @gplusAuthSteps[3].done = true - @renderGPlusAuthChecklist() - renderGPlusAuthChecklist: -> - template = require 'templates/modal/auth-modal-gplus-checklist' + template = require 'templates/core/auth-modal-gplus-checklist' el = $(template({steps: @gplusAuthSteps})) el.i18n() @$el.find('.modal-body:visible').empty().append(el) diff --git a/app/views/core/CocoView.coffee b/app/views/core/CocoView.coffee index 2e51b8d48..d580ae0f7 100644 --- a/app/views/core/CocoView.coffee +++ b/app/views/core/CocoView.coffee @@ -1,8 +1,8 @@ SuperModel = require 'models/SuperModel' utils = require 'core/utils' CocoClass = require 'core/CocoClass' -loadingScreenTemplate = require 'templates/loading' -loadingErrorTemplate = require 'templates/loading_error' +loadingScreenTemplate = require 'templates/core/loading' +loadingErrorTemplate = require 'templates/core/loading-error' lastToggleModalCall = 0 visibleModal = null @@ -49,6 +49,7 @@ module.exports = class CocoView extends Backbone.View @listenTo(@supermodel, 'loaded-all', @onLoaded) @listenTo(@supermodel, 'update-progress', @updateProgress) @listenTo(@supermodel, 'failed', @onResourceLoadFailed) + @warnConnectionError = _.throttle(@warnConnectionError, 3000) super options @@ -59,6 +60,7 @@ module.exports = class CocoView extends Backbone.View @undelegateEvents() # removes both events and subs view.destroy() for id, view of @subviews $('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed + @$el.find('.has-tooltip, [data-original-title]').tooltip 'destroy' @endHighlight() @getPointer(false).remove() @[key] = undefined for key, value of @ @@ -119,7 +121,7 @@ module.exports = class CocoView extends Backbone.View getRenderData: (context) -> context ?= {} - context.isProduction = document.location.href.search(/codecombat.com/) isnt -1 + context.isProduction = application.isProduction() context.me = me context.pathname = document.location.pathname # like '/play/level' context.fbRef = context.pathname.replace(/[^a-zA-Z0-9+/=\-.:_]/g, '').slice(0, 40) or 'home' @@ -149,6 +151,18 @@ module.exports = class CocoView extends Backbone.View # Error handling for loading onResourceLoadFailed: (e) -> r = e.resource + return if r.jqxhr?.status is 402 # payment-required failures are handled separately + if r.jqxhr?.status is 0 + r.retries ?= 0 + r.retries += 1 + if r.retries > 20 + msg = 'Your computer or our servers appear to be offline. Please try refreshing.' + noty text: msg, layout: 'center', type: 'error', killer: true + return + else + @warnConnectionError() + return _.delay (=> r.load()), 3000 + @$el.find('.loading-container .errors').append(loadingErrorTemplate({ status: r.jqxhr?.status name: r.name @@ -157,6 +171,10 @@ module.exports = class CocoView extends Backbone.View })).i18n() @$el.find('.progress').hide() + warnConnectionError: -> + msg = $.i18n.t 'loading_error.connection_failure', defaultValue: 'Connection failed.' + noty text: msg, layout: 'center', type: 'error', killer: true, timeout: 3000 + onRetryResource: (e) -> res = @supermodel.getResource($(e.target).data('resource-index')) # different views may respond to this call, and not all have the resource to reload @@ -182,6 +200,7 @@ module.exports = class CocoView extends Backbone.View # special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value. elem = $(e.target) return unless elem.data('toggle') is 'coco-modal' + return if elem.attr('disabled') target = elem.data('target') Modal = require 'views/'+target e.stopPropagation() @@ -222,7 +241,7 @@ module.exports = class CocoView extends Backbone.View showLoading: ($el=@$el) -> $el.find('>').addClass('hidden') - $el.append loadingScreenTemplate() + $el.append(loadingScreenTemplate()).i18n() @_lastLoading = $el hideLoading: -> @@ -232,7 +251,7 @@ module.exports = class CocoView extends Backbone.View @_lastLoading = null showReadOnly: -> - return if me.isAdmin() + return if me.isAdmin() or me.isArtisan() warning = $.i18n.t 'editor.read_only_warning2', defaultValue: 'Note: you can\'t save any edits here, because you\'re not logged in.' noty text: warning, layout: 'center', type: 'information', killer: true, timeout: 5000 @@ -378,12 +397,7 @@ module.exports = class CocoView extends Backbone.View # Utilities getQueryVariable: (param, defaultValue) -> CocoView.getQueryVariable(param, defaultValue) - @getQueryVariable: (param, defaultValue) -> - query = document.location.search.substring 1 - pairs = (pair.split('=') for pair in query.split '&') - for pair in pairs when pair[0] is param - return {'true': true, 'false': false}[pair[1]] ? decodeURIComponent(pair[1]) - defaultValue + @getQueryVariable: (param, defaultValue) -> utils.getQueryVariable(param, defaultValue) # Moved to utils; TODO finish migrating getRootView: -> view = @ @@ -405,6 +419,12 @@ module.exports = class CocoView extends Backbone.View return @_isIPadApp if @_isIPadApp? return @_isIPadApp = webkit?.messageHandlers? and navigator.userAgent?.indexOf('iPad') isnt -1 + isIPadBrowser: -> + navigator?.userAgent?.indexOf('iPad') isnt -1 + + isFirefox: -> + navigator.userAgent.toLowerCase().indexOf('firefox') isnt -1 + initSlider: ($el, startValue, changeCallback) -> slider = $el.slider({animate: 'fast'}) slider.slider('value', startValue) @@ -442,7 +462,8 @@ module.exports = class CocoView extends Backbone.View playSound: (trigger, volume=1) -> Backbone.Mediator.publish 'audio-player:play-sound', trigger: trigger, volume: volume - mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i +mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i + mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i module.exports = CocoView diff --git a/app/views/core/ContactModal.coffee b/app/views/core/ContactModal.coffee index 84985434d..1a7fa17c3 100644 --- a/app/views/core/ContactModal.coffee +++ b/app/views/core/ContactModal.coffee @@ -1,5 +1,5 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/modal/contact' +template = require 'templates/core/contact' forms = require 'core/forms' {sendContactMessage} = require 'core/contact' @@ -32,6 +32,19 @@ module.exports = class ContactModal extends ModalView contactMessage = forms.formToObject @$el res = tv4.validateMultiple contactMessage, contactSchema return forms.applyErrorsToForm @$el, res.errors unless res.valid + @populateBrowserData contactMessage window.tracker?.trackEvent 'Sent Feedback', message: contactMessage sendContactMessage contactMessage, @$el $.post "/db/user/#{me.id}/track/contact_codecombat" + + populateBrowserData: (context) -> + if $.browser + context.browser = "#{$.browser.platform} #{$.browser.name} #{$.browser.versionNumber}" + context.screenSize = "#{screen?.width ? $(window).width()} x #{screen?.height ? $(window).height()}" + context.screenshotURL = @screenshotURL + + updateScreenshot: -> + return unless @screenshotURL + screenshotEl = @$el.find('#contact-screenshot').removeClass('secret') + screenshotEl.find('a').prop('href', @screenshotURL) + screenshotEl.find('img').prop('src', @screenshotURL) diff --git a/app/views/core/DiplomatSuggestionModal.coffee b/app/views/core/DiplomatSuggestionModal.coffee index d16de5d60..320ce12bf 100644 --- a/app/views/core/DiplomatSuggestionModal.coffee +++ b/app/views/core/DiplomatSuggestionModal.coffee @@ -1,5 +1,5 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/modal/diplomat_suggestion' +template = require 'templates/core/diplomat-suggestion' {me} = require 'core/auth' forms = require 'core/forms' @@ -15,4 +15,11 @@ module.exports = class DiplomatSuggestionModal extends ModalView me.patch() $('#email_translator').prop('checked', 1) @hide() - return + noty { + text: $.i18n.t 'account_settings.saved' + layout: 'topCenter' + timeout: 5000 + type: 'information' + } + Backbone.Mediator.publish 'router:navigate', + route: "/contribute/diplomat" diff --git a/app/views/core/ModalView.coffee b/app/views/core/ModalView.coffee index e3290c137..0a5b8afc3 100644 --- a/app/views/core/ModalView.coffee +++ b/app/views/core/ModalView.coffee @@ -7,7 +7,7 @@ module.exports = class ModalView extends CocoView modalWidthPercent: null plain: false instant: false - template: require 'templates/modal/modal_base' + template: require 'templates/core/modal-base' events: 'click a': 'toggleModal' diff --git a/app/views/modal/RecoverModal.coffee b/app/views/core/RecoverModal.coffee similarity index 96% rename from app/views/modal/RecoverModal.coffee rename to app/views/core/RecoverModal.coffee index bd435a714..21358a634 100644 --- a/app/views/modal/RecoverModal.coffee +++ b/app/views/core/RecoverModal.coffee @@ -1,5 +1,5 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/modal/recover' +template = require 'templates/core/recover-modal' forms = require 'core/forms' {genericFailure} = require 'core/errors' diff --git a/app/views/core/RootView.coffee b/app/views/core/RootView.coffee index cdaf00de4..aee65bd78 100644 --- a/app/views/core/RootView.coffee +++ b/app/views/core/RootView.coffee @@ -36,35 +36,41 @@ module.exports = class RootView extends CocoView 'modal:open-modal-view': 'onOpenModalView' showNewAchievement: (achievement, earnedAchievement) -> + earnedAchievement.set('notified', true) + earnedAchievement.patch() return if achievement.get('collection') is 'level.sessions' - popup = new AchievementPopup achievement: achievement, earnedAchievement: earnedAchievement + #return if @isIE() # Some bugs in IE right now, TODO fix soon! # Maybe working now with not caching achievement fetches in CocoModel? + new AchievementPopup achievement: achievement, earnedAchievement: earnedAchievement handleNewAchievements: (e) -> _.each e.earnedAchievements.models, (earnedAchievement) => achievement = new Achievement(_id: earnedAchievement.get('achievement')) achievement.fetch - success: (achievement) => @showNewAchievement(achievement, earnedAchievement) + success: (achievement) => @showNewAchievement?(achievement, earnedAchievement) + cache: false logoutAccount: -> - Backbone.Mediator.publish("auth:logging-out") + Backbone.Mediator.publish("auth:logging-out", {}) window.tracker?.trackEvent 'Log Out', category:'Homepage', ['Google Analytics'] if @id is 'home-view' logoutUser($('#login-email').val()) - showWizardSettingsModal: -> - WizardSettingsModal = require('views/modal/WizardSettingsModal') - subview = new WizardSettingsModal {} - @openModalView subview - onClickSignupButton: -> AuthModal = require 'views/core/AuthModal' - window.tracker?.trackEvent 'Sign Up', category: 'Homepage', ['Google Analytics'] if @id is 'home-view' + switch @id + when 'home-view' + window.tracker?.trackEvent 'Started Signup', category: 'Homepage', label: 'Homepage' + when 'world-map-view' + # TODO: add campaign data + window.tracker?.trackEvent 'Started Signup', category: 'World Map', label: 'World Map' + else + window.tracker?.trackEvent 'Started Signup', label: @id @openModalView new AuthModal {mode: 'signup'} - + onClickLoginButton: -> AuthModal = require 'views/core/AuthModal' window.tracker?.trackEvent 'Login', category: 'Homepage', ['Google Analytics'] if @id is 'home-view' @openModalView new AuthModal {mode: 'login'} - + onClickAnchor: (e) -> return if @destroyed anchorText = e?.currentTarget?.text @@ -98,7 +104,7 @@ module.exports = class RootView extends CocoView @$el.addClass('site-chrome') if @showBackground @$el.addClass('show-background') - + super(arguments...) @chooseTab(location.hash.replace('#', '')) if location.hash @buildLanguages() @@ -130,22 +136,25 @@ module.exports = class RootView extends CocoView genericCodes = _.filter codes, (code) -> _.find(codes, (code2) -> code2 isnt code and code2.split('-')[0] is code) - for code, localeInfo of locale when not (code in genericCodes) or code is initialVal + for code, localeInfo of locale when code isnt 'update' and (not (code in genericCodes) or code is initialVal) $select.append( $('<option></option>').val(code).text(localeInfo.nativeDescription)) + if code is 'fr' + $select.append( + $('<option class="select-dash" disabled="disabled"></option>').text('----------------------------------')) $select.val(initialVal) onLanguageChanged: -> newLang = $('.language-dropdown').val() $.i18n.setLng(newLang, {}) @saveLanguage(newLang) - + loading = application.moduleLoader.loadLanguage(me.get('preferredLanguage', true)) if loading @listenToOnce application.moduleLoader, 'load-complete', @onLanguageLoaded else @onLanguageLoaded() - + onLanguageLoaded: -> @render() unless me.get('preferredLanguage').split('-')[0] is 'en' diff --git a/app/views/core/SubscribeModal.coffee b/app/views/core/SubscribeModal.coffee new file mode 100644 index 000000000..d9e3ee8a1 --- /dev/null +++ b/app/views/core/SubscribeModal.coffee @@ -0,0 +1,174 @@ +ModalView = require 'views/core/ModalView' +template = require 'templates/core/subscribe-modal' +stripeHandler = require 'core/services/stripe' +utils = require 'core/utils' +AuthModal = require 'views/core/AuthModal' + +module.exports = class SubscribeModal extends ModalView + id: 'subscribe-modal' + template: template + plain: true + closesOnClickOutside: false + product: + amount: 999 + planID: 'basic' + + subscriptions: + 'stripe:received-token': 'onStripeReceivedToken' + + events: + 'click #close-modal': 'hide' + 'click .popover-content .parent-send': 'onClickParentSendButton' + 'click .email-parent-complete button': 'onClickParentEmailCompleteButton' + 'click .purchase-button': 'onClickPurchaseButton' + + constructor: (options) -> + super(options) + @state = 'standby' + + getRenderData: -> + c = super() + c.state = @state + c.stateMessage = @stateMessage + c.price = @product.amount / 100 + #c.price = 3.99 # Sale + return c + + afterRender: -> + super() + @setupParentButtonPopover() + @setupParentInfoPopover() + @setupPaymentMethodsInfoPopover() + + setupParentButtonPopover: -> + popoverTitle = $.i18n.t 'subscribe.parent_email_title' + popoverTitle += '<button type="button" class="close" onclick="$('.parent-button').popover('hide');">×</button>' + popoverContent = -> + console.log 'found html', $('.parent-button-popover-content').html() + $('.parent-button-popover-content').html() + @$el.find('.parent-button').popover( + animation: true + html: true + placement: 'top' + trigger: 'click' + title: popoverTitle + content: popoverContent + container: @$el + ).on 'shown.bs.popover', => + application.tracker?.trackEvent 'Subscription ask parent button click' + + setupParentInfoPopover: -> + popoverTitle = $.i18n.t 'subscribe.parents_title' + levelsCompleted = me.get('stats')?.gamesCompleted or 'several' + popoverContent = "<p>" + $.i18n.t('subscribe.parents_blurb1', nLevels: levelsCompleted) + "</p>" + popoverContent += "<p>" + $.i18n.t('subscribe.parents_blurb1a') + "</p>" + popoverContent += "<p>" + $.i18n.t('subscribe.parents_blurb2') + "</p>" + popoverContent += "<p>" + $.i18n.t('subscribe.parents_blurb3') + "</p>" + #popoverContent = popoverContent.replace /9[.,]99/g, '3.99' # Sale + @$el.find('#parents-info').popover( + animation: true + html: true + placement: 'top' + trigger: 'hover' + title: popoverTitle + content: popoverContent + container: @$el + ).on 'shown.bs.popover', => + application.tracker?.trackEvent 'Subscription parent hover' + + setupPaymentMethodsInfoPopover: -> + popoverTitle = $.i18n.t('subscribe.payment_methods_title') + popoverTitle += '<button type="button" class="close" onclick="$('#payment-methods-info').popover('hide');">×</button>' + popoverContent = "<p>" + $.i18n.t('subscribe.payment_methods_blurb1') + "</p>" + popoverContent += "<p>" + $.i18n.t('subscribe.payment_methods_blurb2') + " <a href='mailto:support@codecombat.com'>support@codecombat.com</a>." + @$el.find('#payment-methods-info').popover( + animation: true + html: true + placement: 'top' + trigger: 'click' + title: popoverTitle + content: popoverContent + container: @$el + ).on 'shown.bs.popover', => + application.tracker?.trackEvent 'Subscription payment methods hover' + + onClickParentSendButton: (e) -> + # TODO: Popover sometimes dismisses immediately after send + + email = @$el.find('.popover-content .parent-input').val() + unless /[\w\.]+@\w+\.\w+/.test email + @$el.find('.popover-content .parent-input').parent().addClass('has-error') + @$el.find('.popover-content .parent-email-validator').show() + return false + + request = @supermodel.addRequestResource 'send_one_time_email', { + url: '/db/user/-/send_one_time_email' + data: {email: email, type: 'subscribe modal parent'} + method: 'POST' + }, 0 + request.load() + + @$el.find('.popover-content .email-parent-form').hide() + @$el.find('.popover-content .email-parent-complete').show() + false + + onClickParentEmailCompleteButton: (e) -> + @$el.find('.parent-button').popover('hide') + + onClickPurchaseButton: (e) -> + @playSound 'menu-button-click' + return @openModalView new AuthModal() if me.get('anonymous') + application.tracker?.trackEvent 'Started subscription purchase' + options = { + description: $.i18n.t('subscribe.stripe_description') + amount: @product.amount + alipay: if me.get('chinaVersion') or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto' + alipayReusable: true + } + + # SALE LOGIC + # overwrite amount with sale price + # maybe also put in another description with details about how long it lasts, etc + # NOTE: Do not change this price without updating the context.price in getRenderData + # NOTE: And, the popover content if necessary + #options = { + # description: 'Monthly Subscription (HoC sale)' + # amount: 399 + #} + + @purchasedAmount = options.amount + + stripeHandler.open(options) + + onStripeReceivedToken: (e) -> + @state = 'purchasing' + @render() + + stripe = _.clone(me.get('stripe') ? {}) + stripe.planID = @product.planID + stripe.token = e.token.id + me.set 'stripe', stripe + + @listenToOnce me, 'sync', @onSubscriptionSuccess + @listenToOnce me, 'error', @onSubscriptionError + me.patch({headers: {'X-Change-Plan': 'true'}}) + + onSubscriptionSuccess: -> + application.tracker?.trackEvent 'Finished subscription purchase', value: @purchasedAmount + Backbone.Mediator.publish 'subscribe-modal:subscribed', {} + @playSound 'victory' + @hide() + + onSubscriptionError: (user, response, options) -> + console.error 'We got an error subscribing with Stripe from our server:', response + stripe = me.get('stripe') ? {} + delete stripe.token + delete stripe.planID + # TODO: Need me.set('stripe', stripe) here? + xhr = options.xhr + if xhr.status is 402 + @state = 'declined' + else + @state = 'unknown_error' + @stateMessage = "#{xhr.status}: #{xhr.responseText}" + @render() diff --git a/app/views/courses/mock1/CourseDetailsView.coffee b/app/views/courses/mock1/CourseDetailsView.coffee new file mode 100644 index 000000000..dd9ec574b --- /dev/null +++ b/app/views/courses/mock1/CourseDetailsView.coffee @@ -0,0 +1,197 @@ +app = require 'core/application' +RootView = require 'views/core/RootView' +template = require 'templates/courses/mock1/course-details' +CocoCollection = require 'collections/CocoCollection' +Campaign = require 'models/Campaign' + +module.exports = class CourseDetailsView extends RootView + id: 'course-details-view' + template: template + + events: + 'change .expand-progress-checkbox': 'onExpandedProgressCheckbox' + 'change .student-mode-checkbox': 'onChangeStudent' + 'click .btn-play-level': 'onClickPlayLevel' + 'click .btn-save-settings': 'onClickSaveSettings' + 'click .member-header': 'onClickMemberHeader' + 'click .progress-header': 'onClickProgressHeader' + 'mouseenter .progress-level-cell': 'onMouseEnterPoint' + 'mouseleave .progress-level-cell': 'onMouseLeavePoint' + + constructor: (options, @courseID=0, @instanceID=0) -> + super options + @initData() + + destroy: -> + @stopListening?() + + getRenderData: -> + context = super() + context.conceptsProgression = @conceptsProgression ? [] + context.course = @course ? {} + context.courseConcepts = @courseConcepts ? [] + context.instance = @instances?[@currentInstanceIndex] ? {} + context.instances = @instances ? [] + context.levelConceptsMap = @levelConceptsMap ? {} + context.maxLastStartedIndex = @maxLastStartedIndex ? 0 + context.memberSort = @memberSort + context.userConceptsMap = @userConceptsMap ? {} + context.userLevelStateMap = @userLevelStateMap ? {} + context.showExpandedProgress = @course.levels.length <= 30 or @showExpandedProgress + context.studentMode = @options.studentMode ? false + + conceptsCompleted = {} + for user of context.userConceptsMap + for concept of context.userConceptsMap[user] + conceptsCompleted[concept] ?= 0 + conceptsCompleted[concept]++ + context.conceptsCompleted = conceptsCompleted + + stats = + averageLevelPlaytime: _.random(30, 240) + averageLevelsCompleted: _.random(1, @course.levels.length) + stats.totalPlayTime = context.instance.students?.length * stats.averageLevelPlaytime ? 0 + stats.totalLevelsCompleted = context.instance.students?.length * stats.averageLevelsCompleted ? 0 + stats.lastLevelCompleted = @course.levels[@maxLastStartedIndex] ? @course.levels[@course.levels.length - 1] + context.stats = stats + + context + + initData: -> + @memberSort = 'nameAsc' + mockData = require 'views/courses/mock1/CoursesMockData' + @course = mockData.courses[@courseID] + @currentInstanceIndex = @instanceID + @instances = mockData.instances + @updateLevelMaps() + + @campaigns = new CocoCollection([], { url: "/db/campaign", model: Campaign, comparator:'_id' }) + @listenTo @campaigns, 'sync', @onCampaignSync + @supermodel.loadModel @campaigns, 'clan', cache: false + + updateLevelMaps: -> + @levelMap = {} + @levelMap[level] = true for level in @course.levels + @userLevelStateMap = {} + @maxLastStartedIndex = -1 + for student in @instances?[@currentInstanceIndex].students + @userLevelStateMap[student] = {} + lastCompletedIndex = _.random(-1, @course.levels.length) + for i in [0..lastCompletedIndex] + @userLevelStateMap[student][@course.levels[i]] = 'complete' + lastStartedIndex = lastCompletedIndex + 1 + @userLevelStateMap[student][@course.levels[lastStartedIndex]] = 'started' + @maxLastStartedIndex = lastStartedIndex if lastStartedIndex > @maxLastStartedIndex + @sortMembers() + + sortMembers: -> + # Progress sort precedence: most completed concepts, most started concepts, most levels, name sort + instance = @instances?[@currentInstanceIndex] ? {} + return if _.isEmpty(instance) + switch @memberSort + when "nameDesc" + instance.students.sort (a, b) -> b.localeCompare(a) + when "progressAsc" + instance.students.sort (a, b) => + for level in @course.levels + if @userLevelStateMap[a][level] isnt 'complete' and @userLevelStateMap[b][level] is 'complete' + return -1 + else if @userLevelStateMap[a][level] is 'complete' and @userLevelStateMap[b][level] isnt 'complete' + return 1 + 0 + when "progressDesc" + instance.students.sort (a, b) => + for level in @course.levels + if @userLevelStateMap[a][level] isnt 'complete' and @userLevelStateMap[b][level] is 'complete' + return 1 + else if @userLevelStateMap[a][level] is 'complete' and @userLevelStateMap[b][level] isnt 'complete' + return -1 + 0 + else + instance.students.sort (a, b) -> a.localeCompare(b) + + onCampaignSync: -> + return unless @campaigns.loaded + @conceptsProgression = [] + @courseConcepts = [] + @levelConceptsMap = {} + @levelNameSlugMap = {} + @userConceptsMap = {} + # Update course levels if course has a specific campaign + for campaign in @campaigns.models when campaign.get('slug') is @course.campaign + @course.levels = [] + for levelID, level of campaign.get('levels') + if campaign.get('slug') is @course.campaign + @course.levels.push level.name + @updateLevelMaps() + + for campaign in @campaigns.models + continue if campaign.get('slug') is 'auditions' + for levelID, level of campaign.get('levels') + @levelNameSlugMap[level.name] = level.slug + if level.concepts? + for concept in level.concepts + @conceptsProgression.push concept unless concept in @conceptsProgression + continue unless @levelMap[level.name] + @courseConcepts.push concept unless concept in @courseConcepts + @levelConceptsMap[level.name] ?= {} + @levelConceptsMap[level.name][concept] = true + for student in @instances?[@currentInstanceIndex].students + @userConceptsMap[student] ?= {} + if @userLevelStateMap[student][level.name] is 'complete' + @userConceptsMap[student][concept] = 'complete' + else if @userLevelStateMap[student][level.name] is 'started' + @userConceptsMap[student][concept] ?= 'started' + @courseConcepts.sort (a, b) => if @conceptsProgression.indexOf(a) < @conceptsProgression.indexOf(b) then -1 else 1 + @render?() + + onChangeStudent: (e) -> + @options.studentMode = $('.student-mode-checkbox').prop('checked') + @render?() + $('.student-mode-checkbox').attr('checked', @options.studentMode) + + onExpandedProgressCheckbox: (e) -> + @showExpandedProgress = $('.expand-progress-checkbox').prop('checked') + # TODO: why does render reset the checkbox to be unchecked? + @render?() + $('.expand-progress-checkbox').attr('checked', @showExpandedProgress) + + onClickMemberHeader: (e) -> + @memberSort = if @memberSort is 'nameAsc' then 'nameDesc' else 'nameAsc' + @sortMembers() + @render?() + + onClickProgressHeader: (e) -> + @memberSort = if @memberSort is 'progressAsc' then 'progressDesc' else 'progressAsc' + @sortMembers() + @render?() + + onClickPlayLevel: (e) -> + levelName = $(e.target).data('level') + levelSlug = @levelNameSlugMap[levelName] + Backbone.Mediator.publish 'router:navigate', { + route: "/play/level/#{levelSlug}" + viewClass: 'views/play/level/PlayLevelView' + viewArgs: [{}, levelSlug] + } + + onClickSaveSettings: (e) -> + if name = $('.edit-name-input').val() + @instances[@currentInstanceIndex].name = name + description = $('.edit-description-input').val() + @instances[@currentInstanceIndex].description = description + $('#editSettingsModal').modal('hide') + @render?() + + onMouseEnterPoint: (e) -> + $('.level-popup-container').hide() + container = $(e.target).find('.level-popup-container').show() + margin = 20 + offset = $(e.target).offset() + scrollTop = $(e.target).offsetParent().scrollTop() + height = container.outerHeight() + container.css('left', offset.left + e.offsetX) + container.css('top', offset.top + scrollTop - height - margin) + + onMouseLeavePoint: (e) -> + $(e.target).find('.level-popup-container').hide() diff --git a/app/views/courses/mock1/CourseEnrollView.coffee b/app/views/courses/mock1/CourseEnrollView.coffee new file mode 100644 index 000000000..ae745068a --- /dev/null +++ b/app/views/courses/mock1/CourseEnrollView.coffee @@ -0,0 +1,63 @@ +app = require 'core/application' +RootView = require 'views/core/RootView' +template = require 'templates/courses/mock1/course-enroll' + +module.exports = class CourseEnrollView extends RootView + id: 'course-enroll-view' + template: template + + events: + 'click .btn-buy': 'onClickBuy' + 'change .course-select': 'onChangeCourse' + 'change .input-quantity': 'onQuantityChange' + + constructor: (options, @courseID=0) -> + super options + @initData() + + getRenderData: -> + context = super() + context.courses = @courses ? {} + context.courseID = @courseID + context.price = @price ? 0 + context.quantity = @quantity + context.selectedCourseTitle = @selectedCourseTitle + context + + afterRender: -> + super() + @$el.find('.course-select').val(@selectedCourseTitle) + + initData: -> + mockData = require 'views/courses/mock1/CoursesMockData' + @courses = mockData.courses + @selectedCourseTitle = @courses[@courseID]?.title + @quantity = 20 + @updatePrice() + + onClickBuy: (e) -> + if @selectedCourseTitle is 'All Courses' + app.router.navigate "/courses/mock1/0" + else + for course, i in @courses when course.title is @selectedCourseTitle + app.router.navigate "/courses/mock1/#{i}" + break + window.location.reload() + + onChangeCourse: (e) -> + @selectedCourseTitle = $(e.target).val() + @updatePrice() + @render?() + + onQuantityChange: (e) -> + @quantity = $(e.target).val() ? 20 + @updatePrice() + @render?() + + updatePrice: -> + if @selectedCourseTitle is 'All Courses' + @price = (@courses.length - 1) * @quantity * 2 + else if @selectedCourseTitle is 'Introduction to Computer Science' + @price = 0 + else + @price = @quantity * 4 diff --git a/app/views/courses/mock1/CourseInfoView.coffee b/app/views/courses/mock1/CourseInfoView.coffee new file mode 100644 index 000000000..e047c0883 --- /dev/null +++ b/app/views/courses/mock1/CourseInfoView.coffee @@ -0,0 +1,31 @@ +app = require 'core/application' +RootView = require 'views/core/RootView' +template = require 'templates/courses/mock1/course-info' + +module.exports = class CourseInfoView extends RootView + id: 'course-info-view' + template: template + + events: + 'click .btn-enroll': 'onClickEnroll' + + constructor: (options, @courseID) -> + super options + @initData() + + getRenderData: -> + context = super() + context.course = @course ? {} + context.courseID = @courseID + context.praise = @praise + context + + initData: -> + mockData = require 'views/courses/mock1/CoursesMockData' + @course = mockData.courses[@courseID] + @praise = mockData.praise[_.random(0, mockData.praise.length - 1)] + + onClickEnroll: (e) -> + courseID = $(e.target).data('course-id') + app.router.navigate "/courses/mock1/#{courseID}/enroll" + window.location.reload() diff --git a/app/views/courses/mock1/CoursesMockData.coffee b/app/views/courses/mock1/CoursesMockData.coffee new file mode 100644 index 000000000..26d58fd47 --- /dev/null +++ b/app/views/courses/mock1/CoursesMockData.coffee @@ -0,0 +1,170 @@ +data = {} + +data.concepts = [ + 'Advanced Strings' + 'Algorithms' + 'Arithmetic' + 'Arrays' + 'Basic Syntax' + 'Boolean Logic' + 'Break Statements' + 'Classes' + 'For Loops' + 'Functions' + 'If Statements' + 'Input Handling' + 'Math Operations' + 'Object Literals' + 'Strings' + 'Variables' + 'Vectors' + 'While Loops' +] + +data.courses = [ + { + title: 'Introduction to Computer Science' + description: 'Learn basic syntax, while loops, and the CodeCombat learning environment.' + topics: ['Basic Syntax', 'Strings', 'Loops'] + duration: 1 + levels: ['Dungeons of Kithgard', 'Gems in the Deep', 'Shadow Guard', 'Kounter Kithwise', 'Crawlways of Kithgard', 'Enemy Mine', 'Illusory Interruption', 'Forgetful Gemsmith', 'Signs and Portents', 'Favorable Odds', 'True Names', 'The Prisoner', 'Banefire', 'The Raised Sword', 'Haunted Kithmaze', 'Riddling Kithmaze', 'Descending Further', 'The Second Kithmaze', 'Dread Door', 'Cupboards of Kithgard', 'Hack and Dash'] + campaign: 'intro' + image: '/images/pages/courses/101_info.png' + }, + { + title: 'Computer Science 2' + description: 'Introduce Arguments, Variables, If Statements, and Arithmetic.' + topics: ['Arguments', 'Variables', 'If Statements', 'Arithmetic'] + duration: 5 + levels: ['Known Enemy', 'Master of Names', 'Lowly Kithmen', 'Closing the Distance', 'Tactical Strike', 'The Final Kithmaze', 'The Gauntlet', 'Radiant Aura', 'Kithgard Gates', 'Destroying Angel', 'Deadly Dungeon Rescue', 'Kithgard Brawl', 'Cavern Survival', 'Breakout', 'Attack Wisely!', 'Kithgard Mastery', 'Kithgard Apprentice', 'Long Kithmaze', 'Boom! and Bust', 'Defense of Plainswood', 'Winding Trail', 'Thumb Biter', 'Gems or Death', 'Backwoods Ambush', 'Patrol Buster', 'Endangered Burl', 'Village Guard', 'Thornbush Farm', 'Back to Back', 'Ogre Encampment', 'Woodland Cleaver', 'Shield Rush', 'Peasant Protection', 'Munchkin Swarm'] + image: '/images/pages/courses/102_info.png' + }, + { + title: 'Computer Science 3' + description: 'Learn how to handle input.' + topics: ['If Statements', 'Arithmetic', 'Input Handling'] + duration: 5 + levels: ['Munchkin Harvest', 'Swift Dagger', 'Shrapnel', 'Arcane Ally', 'Touch of Death', 'Bonemender', 'Coinucopia', 'Copper Meadows', 'Drop the Flag', 'Deadly Pursuit', 'Rich Forager', 'Siege of Stonehold', 'Multiplayer Treasure Grove', 'Dueling Grounds', 'Backwoods Brawl', 'Backwoods Treasure', 'Range Finder', 'Stillness in Motion', 'The Agrippa Defense', 'Storming the Towers of Areth', 'Hold the Forest Pass', 'Hold for Reinforcements', 'Storming the Farmhouse', 'Wild Horses', 'Boulder Woods', 'Unfair Support', 'Tactical Timing', 'Apocalypse', 'Doom Glade', 'Defend the Garrison', 'Lost Viking', 'Forest Flower Grove', 'The Dunes', 'The Mighty Sand Yak', 'Oasis', 'Sarven Road', 'Sarven Gaps', 'Thunderhooves', 'Medical Attention', 'The Great Yak Stampede', 'Minesweeper', 'Sarven Sentry', 'Keeping Time'] + image: '/images/pages/courses/103_info.png' + }, + { + title: 'Computer Science 4' + description: 'Time to tackle arrays and some pvp stuff.' + topics: ['Loops', 'Break Statements', 'Arrays'] + duration: 5 + levels: ['Hoarding Gold', 'Decoy Drill', 'Yakstraction', 'Sarven Brawl', 'Desert Combat', 'Dust', 'Sarven Rescue', 'Sacred Statue', 'Mirage Maker', 'Sarven Savior', 'Odd Sandstorm', 'Lurkers', 'Preferential Treatment', 'Sarven Shepherd', 'Shine Getter', 'The Trials', 'Mad Maxer', 'Mad Maxer Strikes Back', 'Mad Maxer Sells Out', 'Mad Maxer Gets Greedy', 'Mad Maxer: Redemption', 'Sarven Treasure', 'Harrowland', 'Sarven Siege', 'Clash of Clones', 'Sand Snakes', 'Crag Tag'] + image: '/images/pages/courses/104_info.png' + }, + { + title: 'Computer Science 5' + description: 'Time to tackle arrays and some PVP.' + topics: ['Break Statements', 'Arrays', 'Object Literals'] + duration: 5 + levels: ['Slalom', 'Black Diamond', 'Treasure Cave', 'Ogre Gorge Gouger', 'Dance-Off', 'Alpine Rally', 'Cloudrip Commander', 'Mountain Mercenaries'] + image: '/images/pages/courses/105_info.png' + }, + { + title: 'Computer Science 6' + description: 'For loops!' + topics: ['Break Statements', 'Object Literals', 'For loops'] + duration: 5 + levels: ['Timber Guard', 'Hunting Party', 'Zoo Keeper', 'Cloudrip Brawl', 'Cloudrip Treasure', 'Cloudrip Siege', 'Noble Sacrifice', 'Zero Sum', 'Borrowed Sword', 'Protect and Serve'] + image: '/images/pages/courses/106_info.png' + }, + { + title: 'Computer Science 7' + description: 'Functions!' + topics: ['Object Literals', 'For loops', 'Functions'] + duration: 5 + levels: ['Vital Powers', 'Timber Turncoat', 'Restless Dead', 'Ring Bearer', 'The Two Flowers', 'The Geometry of Flowers', 'Mountain Flower Grove', 'Hunters and Prey', 'Library Tactician'] + image: '/images/pages/courses/107_info.png' + }, + { + title: 'Computer Science 8' + description: 'Maths.' + topics: ['For loops', 'Functions', 'Math Operations'] + duration: 5 + levels: ['Steelclaw Gap', 'Pesky Yaks', 'Mixed Unit Tactics', 'Sowing Fire', 'Reaping Fire', 'Toil and Trouble', 'What in Carnation', 'Misty Island Mine', 'Raiders of the Long Dark', 'Grim Determination', 'Deadly Discs', "Summit's Gate"] + image: '/images/pages/courses/107_info.png' + }, + { + title: 'Computer Science 9' + description: 'Vectors and strings.' + topics: ['Vectors', 'Advanced Strings'] + duration: 5 + levels: ['Circle Walking', 'Skating Away', 'Kelvintaph Crusader', 'Kelvintaph Burgler', 'Ice Soccer', 'Razorfray'] + image: '/images/pages/courses/107_info.png' + } +] + +getStudents = -> + students = ['Jill', 'Billy', 'Sarah', 'Tom', 'June', 'Bob', 'Kristin', 'Samantha', 'Eric'] + _.shuffle(students).slice(_.random(0, 5)) + +data.instances = [ + { + name: "Mr. Smith's First Period" + description: "Homework due on Friday." + code: 'b2KF7' + students: getStudents() + }, + { + name: "Mr. Smith's Second Period" + description: "Test class description" + code: 'b2KF7' + students: getStudents() + }, + { + name: "Summer Camp 2015" + description: "You should have received an email with extra credit homework." + code: 'b2KF7' + students: getStudents() + }, + { + name: "Maple High 4th" + code: 'b2KF7' + students: getStudents() + }, + { + name: "Test class name one" + description: "Test class description" + code: 'b2KF7' + students: getStudents() + } +] + +data.praise = [ + { + quote: "The kids love it." + source: "Leo Joseph Tran, Athlos Leadership Academy" + }, + { + quote: "My students have been using the site for a couple of weeks and they love it." + source: "Scott Hatfield, Computer Applications Teacher, School Technology Coordinator, Eastside Middle School" + }, + { + quote: "Thanks for the captivating site. My eighth graders love it." + source: "Janet Cook, Ansbach Middle/High School" + }, + { + quote: "My students have started working on CodeCombat and love it! I love that they are learning coding and problem solving skills without them even knowing it!!" + source: "Kristin Huff, Special Education Teacher, Webb City School District" + }, + { + quote: "I recently introduced Code Combat to a few of my fifth graders and they are loving it!" + source: "Shauna Hamman, Fifth Grade Teacher, Four Peaks Elementary School" + }, + { + quote: "Overall I think it's a fantastic service. Variables, arrays, loops, all covered in very fun and imaginative ways. Every kid who has tried it is a fan." + source: "Aibinder Andrew, Technology Teacher" + }, + { + quote: "I love what you have created. The kids are so engaged." + source: "Desmond Smith, 4KS Academy" + }, + { + quote: "My students love the website and I hope on having content structured around it in the near future." + source: "Michael Leonard, Science Teacher, Clearwater Central Catholic High School" + } +] +module.exports = data diff --git a/app/views/courses/mock1/CoursesView.coffee b/app/views/courses/mock1/CoursesView.coffee new file mode 100644 index 000000000..b6f49c607 --- /dev/null +++ b/app/views/courses/mock1/CoursesView.coffee @@ -0,0 +1,75 @@ +app = require 'core/application' +RootView = require 'views/core/RootView' +template = require 'templates/courses/mock1/courses' + +module.exports = class CoursesView extends RootView + id: 'courses-view' + template: template + + events: + 'click .btn-buy': 'onClickBuy' + 'click .btn-continue': 'onClickContinue' + 'click .btn-enroll': 'onClickEnroll' + 'click .btn-enter': 'onClickEnter' + 'hidden.bs.modal #continueModal': 'onHideContinueModal' + + constructor: (options) -> + super options + @initData() + + getRenderData: -> + context = super() + context.courses = @courses ? [] + context.instances = @instances ? [] + context.praise = @praise + context + + initData: -> + mockData = require 'views/courses/mock1/CoursesMockData' + @courses = mockData.courses + for course, i in @courses + if _.random(0, 2) > 0 + course.unlocked = true + else + break + @instances = mockData.instances + @praise = mockData.praise[_.random(0, mockData.praise.length - 1)] + + onClickBuy: (e) -> + $('#continueModal').modal('hide') + courseID = $(e.target).data('course-id') ? 0 + viewClass = require 'views/courses/mock1/CourseEnrollView' + viewArgs = [{}, courseID] + navigationEvent = route: "/courses/mock1/enroll", viewClass: viewClass, viewArgs: viewArgs + Backbone.Mediator.publish 'router:navigate', navigationEvent + + onClickContinue: (e) -> + courseID = $(e.target).data('course-id') + courseTitle = $(e.target).data('course-title') + $('#continueModal').find('.modal-title').text(courseTitle) + $('#continueModal').find('.btn-buy').data('course-id', courseID) + $('#continueModal').find('.btn-enroll').data('course-id', courseID) + $('#continueModal').find('.btn-enter').data('course-id', courseID) + $('#continueModal .row-pick-class').show() if @courses[courseID]?.unlocked + + onClickEnroll: (e) -> + $('#continueModal').modal('hide') + courseID = $(e.target).data('course-id') + instanceID = _.random(0, @instances.length - 1) + viewClass = require 'views/courses/mock1/CourseDetailsView' + viewArgs = [{}, courseID, instanceID] + navigationEvent = route: "/courses/mock1/#{courseID}", viewClass: viewClass, viewArgs: viewArgs + Backbone.Mediator.publish 'router:navigate', navigationEvent + + onClickEnter: (e) -> + $('#continueModal').modal('hide') + courseID = $(e.target).data('course-id') + instanceName = $('.select-session').val() + instanceID = index for val, index in @instances when val.name is instanceName + viewClass = require 'views/courses/mock1/CourseDetailsView' + viewArgs = [{}, courseID, instanceID] + navigationEvent = route: "/courses/mock1/#{courseID}", viewClass: viewClass, viewArgs: viewArgs + Backbone.Mediator.publish 'router:navigate', navigationEvent + + onHideContinueModal: (e) -> + $('#continueModal .row-pick-class').hide() diff --git a/app/views/editor/DeltaView.coffee b/app/views/editor/DeltaView.coffee index 7c9f85048..a01cc9378 100644 --- a/app/views/editor/DeltaView.coffee +++ b/app/views/editor/DeltaView.coffee @@ -1,6 +1,9 @@ CocoView = require 'views/core/CocoView' template = require 'templates/editor/delta' deltasLib = require 'core/deltas' +require 'vendor/diffview' +require 'vendor/difflib' +require 'vendor/treema' TEXTDIFF_OPTIONS = baseTextName: "Old" diff --git a/app/views/editor/ForkModal.coffee b/app/views/editor/ForkModal.coffee index dc0cae36e..e6ab13eb4 100644 --- a/app/views/editor/ForkModal.coffee +++ b/app/views/editor/ForkModal.coffee @@ -6,7 +6,6 @@ module.exports = class ForkModal extends ModalView id: 'fork-modal' template: template instant: false - modalWidthPercent: 60 events: 'click #fork-model-confirm-button': 'forkModel' @@ -29,12 +28,14 @@ module.exports = class ForkModal extends ModalView newModel.unset 'created' newModel.unset 'original' newModel.unset 'parent' + newModel.unset 'i18n' + newModel.unset 'i18nCoverage' newModel.set 'commitMessage', "Forked from #{@model.get('name')}" newModel.set 'name', @$el.find('#fork-model-name').val() if @model.schema().properties.permissions newModel.set 'permissions', [access: 'owner', target: me.id] newPathPrefix = "editor/#{@editorPath}/" - res = newModel.save() + res = newModel.save(null, {type: 'POST'}) # Override PUT so we can trigger postFirstVersion logic return unless res res.error => @hideLoading() diff --git a/app/views/editor/PatchModal.coffee b/app/views/editor/PatchModal.coffee index 99a44661c..9b8b411bd 100644 --- a/app/views/editor/PatchModal.coffee +++ b/app/views/editor/PatchModal.coffee @@ -9,12 +9,17 @@ module.exports = class PatchModal extends ModalView template: template plain: true modalWidthPercent: 60 + instant: true events: 'click #withdraw-button': 'withdrawPatch' 'click #reject-button': 'rejectPatch' 'click #accept-button': 'acceptPatch' + shortcuts: + 'a': 'acceptPatch' + 'r': 'rejectPatch' + constructor: (@patch, @targetModel, options) -> super(options) targetID = @patch.get('target').id diff --git a/app/views/editor/PatchesView.coffee b/app/views/editor/PatchesView.coffee index 576cb6175..feb291197 100644 --- a/app/views/editor/PatchesView.coffee +++ b/app/views/editor/PatchesView.coffee @@ -11,7 +11,7 @@ module.exports = class PatchesView extends CocoView events: 'change .status-buttons': 'onStatusButtonsChanged' - 'click .patch-icon': 'openPatchModal' + 'click .patch-row': 'openPatchModal' constructor: (@model, options) -> super(options) @@ -23,7 +23,7 @@ module.exports = class PatchesView extends CocoView load: -> @initPatches() - @patches = @supermodel.loadCollection(@patches, 'patches').model + @patches = @supermodel.loadCollection(@patches, 'patches', {cache: false}).model @listenTo @patches, 'sync', @onPatchesLoaded onPatchesLoaded: -> @@ -51,8 +51,8 @@ module.exports = class PatchesView extends CocoView @render() openPatchModal: (e) -> - console.log 'open patch modal' - patch = _.find @patches.models, {id: $(e.target).data('patch-id')} + row = $(e.target).closest '.patch-row' + patch = _.find @patches.models, {id: row.data('patch-id')} modal = new PatchModal(patch, @model) @openModalView(modal) @listenTo modal, 'accepted-patch', -> @trigger 'accepted-patch' diff --git a/app/views/editor/achievement/AchievementEditView.coffee b/app/views/editor/achievement/AchievementEditView.coffee index ffdb15549..3fca64907 100644 --- a/app/views/editor/achievement/AchievementEditView.coffee +++ b/app/views/editor/achievement/AchievementEditView.coffee @@ -2,7 +2,8 @@ RootView = require 'views/core/RootView' template = require 'templates/editor/achievement/edit' Achievement = require 'models/Achievement' AchievementPopup = require 'views/core/AchievementPopup' -ConfirmModal = require 'views/modal/ConfirmModal' +ConfirmModal = require 'views/editor/modal/ConfirmModal' +PatchesView = require 'views/editor/PatchesView' errors = require 'core/errors' app = require 'core/application' nodes = require 'views/editor/level/treema_nodes' @@ -16,7 +17,7 @@ module.exports = class AchievementEditView extends RootView 'click #recalculate-button': 'confirmRecalculation' 'click #recalculate-all-button': 'confirmAllRecalculation' 'click #delete-button': 'confirmDeletion' - + constructor: (options, @achievementID) -> super options @achievement = new Achievement(_id: @achievementID) @@ -27,6 +28,9 @@ module.exports = class AchievementEditView extends RootView onLoaded: -> super() @buildTreema() + @listenTo @achievement, 'change', => + @achievement.updateI18NCoverage() + @treema.set('/', @achievement.attributes) buildTreema: -> return if @treema? or (not @achievement.loaded) @@ -50,20 +54,22 @@ module.exports = class AchievementEditView extends RootView getRenderData: (context={}) -> context = super(context) context.achievement = @achievement - context.authorized = me.isAdmin() + context.authorized = me.isAdmin() or me.isArtisan() context afterRender: -> super() return unless @supermodel.finished() @pushChangesToPreview() + @patchesView = @insertSubView(new PatchesView(@achievement), @$el.find('.patches-view')) + @patchesView.load() pushChangesToPreview: => return unless @treema @$el.find('#achievement-view').empty() for key, value of @treema.data @achievement.set key, value - earned = earnedPoints: @achievement.get 'worth' + earned = get: (key) => {earnedPoints: @achievement.get('worth'), previouslyAchievedAmount: 0}[key] popup = new AchievementPopup achievement: @achievement, earnedAchievement: earned, popup: false, container: $('#achievement-view') openSaveModal: -> diff --git a/app/views/editor/achievement/AchievementSearchView.coffee b/app/views/editor/achievement/AchievementSearchView.coffee index 6abf581f4..49d6339b4 100644 --- a/app/views/editor/achievement/AchievementSearchView.coffee +++ b/app/views/editor/achievement/AchievementSearchView.coffee @@ -14,6 +14,6 @@ module.exports = class AchievementSearchView extends SearchView context.currentNew = 'editor.new_achievement_title' context.currentNewSignup = 'editor.new_achievement_title_login' context.currentSearch = 'editor.achievement_search_title' - context.unauthorized = true unless me.isAdmin() - @$el.i18n() + context.newModelsAdminOnly = true + context.unauthorized = true unless me.isAdmin() or me.isArtisan() context diff --git a/app/views/editor/article/ArticleEditView.coffee b/app/views/editor/article/ArticleEditView.coffee index 00902228c..5e2a212d1 100644 --- a/app/views/editor/article/ArticleEditView.coffee +++ b/app/views/editor/article/ArticleEditView.coffee @@ -2,9 +2,10 @@ RootView = require 'views/core/RootView' VersionHistoryView = require './ArticleVersionsModal' template = require 'templates/editor/article/edit' Article = require 'models/Article' -SaveVersionModal = require 'views/modal/SaveVersionModal' +SaveVersionModal = require 'views/editor/modal/SaveVersionModal' PatchesView = require 'views/editor/PatchesView' require 'views/modal/RevertModal' +require 'vendor/treema' module.exports = class ArticleEditView extends RootView id: 'editor-article-edit-view' @@ -28,6 +29,9 @@ module.exports = class ArticleEditView extends RootView onLoaded: -> super() @buildTreema() + @listenTo @article, 'change', => + @article.updateI18NCoverage() + @treema.set('/', @article.attributes) buildTreema: -> return if @treema? or (not @article.loaded) @@ -82,7 +86,7 @@ module.exports = class ArticleEditView extends RootView newArticle = if e.major then @article.cloneNewMajorVersion() else @article.cloneNewMinorVersion() newArticle.set('commitMessage', e.commitMessage) - res = newArticle.save() + res = newArticle.save(null, {type: 'POST'}) # Override PUT so we can trigger postNewVersion logic return unless res modal = @$el.find('#save-version-modal') @enableModalInProgress(modal) diff --git a/app/views/editor/article/ArticleSearchView.coffee b/app/views/editor/article/ArticleSearchView.coffee index 49f22bc64..4f517470c 100644 --- a/app/views/editor/article/ArticleSearchView.coffee +++ b/app/views/editor/article/ArticleSearchView.coffee @@ -14,5 +14,6 @@ module.exports = class ArticleSearchView extends SearchView context.currentNew = 'editor.new_article_title' context.currentNewSignup = 'editor.new_article_title_login' context.currentSearch = 'editor.article_search_title' + context.newModelsAdminOnly = true @$el.i18n() context diff --git a/app/views/editor/article/ArticleVersionsModal.coffee b/app/views/editor/article/ArticleVersionsModal.coffee index 10027ad99..6084ed73b 100644 --- a/app/views/editor/article/ArticleVersionsModal.coffee +++ b/app/views/editor/article/ArticleVersionsModal.coffee @@ -1,4 +1,4 @@ -VersionsModal = require 'views/modal/VersionsModal' +VersionsModal = require 'views/editor/modal/VersionsModal' module.exports = class ArticleVersionsModal extends VersionsModal id: 'editor-article-versions-view' @@ -6,4 +6,4 @@ module.exports = class ArticleVersionsModal extends VersionsModal page: 'article' constructor: (options, @ID) -> - super options, ID, require 'models/Article' + super options, @ID, require 'models/Article' diff --git a/app/views/editor/campaign/CampaignAnalyticsModal.coffee b/app/views/editor/campaign/CampaignAnalyticsModal.coffee new file mode 100644 index 000000000..b4070ee00 --- /dev/null +++ b/app/views/editor/campaign/CampaignAnalyticsModal.coffee @@ -0,0 +1,271 @@ +template = require 'templates/editor/campaign/campaign-analytics-modal' +utils = require 'core/utils' +require 'vendor/d3' +ModalView = require 'views/core/ModalView' + +# TODO: jquery-ui datepicker doesn't work well in this view +# TODO: the date format handling is confusing (yyyy-mm-dd <=> yyyymmdd) + +module.exports = class CampaignAnalyticsModal extends ModalView + id: 'campaign-analytics-modal' + template: template + plain: true + + events: + 'click #reload-button': 'onClickReloadButton' + 'dblclick .level': 'onDblClickLevel' + 'change #option-show-left-game': 'updateShowLeftGame' + 'change #option-show-subscriptions': 'updateShowSubscriptions' + + constructor: (options, @campaignHandle, @campaignCompletions) -> + super options + @showLeftGame = true + @showSubscriptions = true + @getCampaignAnalytics() if me.isAdmin() + + getRenderData: -> + c = super() + c.showLeftGame = @showLeftGame + c.showSubscriptions = @showSubscriptions + c.campaignCompletions = @campaignCompletions + c + + afterRender: -> + super() + $("#input-startday").datepicker dateFormat: "yy-mm-dd" + $("#input-endday").datepicker dateFormat: "yy-mm-dd" + @addCompletionLineGraphs() + + updateShowLeftGame: -> + @showLeftGame = @$el.find('#option-show-left-game').prop('checked') + @render() + + updateShowSubscriptions: -> + @showSubscriptions = @$el.find('#option-show-subscriptions').prop('checked') + @render() + + onClickReloadButton: () => + startDay = $('#input-startday').val() + endDay = $('#input-endday').val() + delete @campaignCompletions.levels + @campaignCompletions.startDay = startDay + @campaignCompletions.endDay = endDay + @render() + @getCampaignAnalytics startDay, endDay + + onDblClickLevel: (e) -> + row = $(e.target).parents('.level') + Backbone.Mediator.publish 'editor:campaign-analytics-modal-closed', targetLevelSlug: row.data 'level-slug' + @hide() + + addCompletionLineGraphs: -> + # TODO: no line graphs if some levels without completion rates? + return unless @campaignCompletions.levels + for level in @campaignCompletions.levels + days = [] + for day of level['days'] + continue unless level['days'][day].started > 0 + days.push + day: day + rate: level['days'][day].finished / level['days'][day].started + count: level['days'][day].started + days.sort (a, b) -> a.day - b.day + data = [] + for i in [0...days.length] + data.push + x: i + y: days[i].rate + c: days[i].count + @addLineGraph '#background' + level.level, data + + addLineGraph: (containerSelector, lineData, lineColor='green', min=0, max=1.0) -> + # Add a line chart to the given container + # Adjust stroke-weight based on segment count: width 0.3 to 3.0 for counts roughly 100 to 10000 + # TODO: Move this to a utility library + vis = d3.select(containerSelector) + width = $(containerSelector).width() + height = $(containerSelector).height() + xRange = d3.scale.linear().range([0, width]).domain([d3.min(lineData, (d) -> d.x), d3.max(lineData, (d) -> d.x)]) + yRange = d3.scale.linear().range([height, 0]).domain([min, max]) + lines = [] + for i in [0...lineData.length-1] + lines.push + x1: xRange(lineData[i].x) + y1: yRange(lineData[i].y) + x2: xRange(lineData[i + 1].x) + y2: yRange(lineData[i + 1].y) + strokeWidth: Math.min(3, Math.max(0.3, Math.log(lineData[i].c/10)/2)) + vis.selectAll('.line') + .data(lines) + .enter() + .append("line") + .attr("x1", (d) -> d.x1) + .attr("y1", (d) -> d.y1) + .attr("x2", (d) -> d.x2) + .attr("y2", (d) -> d.y2) + .style("stroke-width", (d) -> d.strokeWidth) + .style("stroke", lineColor) + + getCampaignAnalytics: (startDay, endDay) => + if startDay? + startDayDashed = startDay + startDay = startDay.replace(/-/g, '') + else + startDay = utils.getUTCDay -14 + startDayDashed = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}" + if endDay? + endDayDashed = endDay + endDay = endDay.replace(/-/g, '') + else + endDay = utils.getUTCDay -1 + endDayDashed = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}" + @campaignCompletions.startDay = startDayDashed + @campaignCompletions.endDay = endDayDashed + + # Chain these together so we can calculate relative metrics (e.g. left game per second) + @getCampaignLevelCompletions startDay, endDay, () => + @render?() + @getCompaignLevelDrops startDay, endDay, () => + @render?() + @getCampaignAveragePlaytimes startDayDashed, endDayDashed, () => + @render?() + @getCampaignLevelSubscriptions startDay, endDay, () => + @render?() + + getCampaignAveragePlaytimes: (startDay, endDay, doneCallback) => + # Fetch level average playtimes + # Needs date format yyyy-mm-dd + success = (data) => + return doneCallback() if @destroyed + # console.log 'getCampaignAveragePlaytimes success', data + levelAverages = {} + maxPlaytime = 0 + for item in data + levelAverages[item.level] ?= [] + levelAverages[item.level].push item.average + for level in @campaignCompletions.levels + if levelAverages[level.level] + if levelAverages[level.level].length > 0 + total = _.reduce levelAverages[level.level], ((sum, num) -> sum + num) + level.averagePlaytime = total / levelAverages[level.level].length + maxPlaytime = level.averagePlaytime if maxPlaytime < level.averagePlaytime + if level.averagePlaytime > 0 and level.dropped > 0 + level.droppedPerSecond = level.dropped / level.averagePlaytime + else + level.averagePlaytime = 0.0 + + addPlaytimePercentage = (item) -> + item.playtimePercentage = Math.round(item.averagePlaytime / maxPlaytime * 100.0) unless maxPlaytime is 0 + item + @campaignCompletions.levels = _.map @campaignCompletions.levels, addPlaytimePercentage, @ + + sortedLevels = _.cloneDeep @campaignCompletions.levels + sortedLevels = _.filter sortedLevels, ((a) -> a.droppedPerSecond > 0), @ + sortedLevels.sort (a, b) -> b.droppedPerSecond - a.droppedPerSecond + @campaignCompletions.top3DropPerSecond = _.pluck sortedLevels[0..2], 'level' + doneCallback() + + levelSlugs = _.pluck @campaignCompletions.levels, 'level' + + request = @supermodel.addRequestResource 'playtime_averages', { + url: '/db/level/-/playtime_averages' + data: {startDay: startDay, endDay: endDay, slugs: levelSlugs} + method: 'POST' + success: success + }, 0 + request.load() + + getCampaignLevelCompletions: (startDay, endDay, doneCallback) => + # Needs date format yyyymmdd + success = (data) => + return doneCallback() if @destroyed + # console.log 'getCampaignLevelCompletions success', data + countCompletions = (item) -> + item.started = _.reduce item.days, ((result, current) -> result + current.started), 0 + item.finished = _.reduce item.days, ((result, current) -> result + current.finished), 0 + item.completionRate = if item.started > 0 then item.finished / item.started * 100 else 0.0 + item + addUserRemaining = (item) -> + item.usersRemaining = Math.round(item.started / maxStarted * 100.0) unless maxStarted is 0 + item + + @campaignCompletions.levels = _.map data, countCompletions, @ + if @campaignCompletions.levels.length > 0 + maxStarted = (_.max @campaignCompletions.levels, ((a) -> a.started)).started + else + maxStarted = 0 + @campaignCompletions.levels = _.map @campaignCompletions.levels, addUserRemaining, @ + + sortedLevels = _.cloneDeep @campaignCompletions.levels + sortedLevels = _.filter sortedLevels, ((a) -> a.finished >= 10), @ + if sortedLevels.length >= 3 + sortedLevels.sort (a, b) -> b.completionRate - a.completionRate + @campaignCompletions.top3 = _.pluck sortedLevels[0..2], 'level' + @campaignCompletions.bottom3 = _.pluck sortedLevels[sortedLevels.length - 4...sortedLevels.length - 1], 'level' + + doneCallback() + + # TODO: Why do we need this url dash? + request = @supermodel.addRequestResource 'campaign_completions', { + url: '/db/analytics_perday/-/campaign_completions' + data: {startDay: startDay, endDay: endDay, slug: @campaignHandle} + method: 'POST' + success: success + }, 0 + request.load() + + getCompaignLevelDrops: (startDay, endDay, doneCallback) => + # Fetch level drops + # Needs date format yyyymmdd + success = (data) => + return if @destroyed + # console.log 'getCompaignLevelDrops success', data + levelDrops = {} + for item in data + levelDrops[item.level] ?= item.dropped + for level in @campaignCompletions.levels + level.dropped = levelDrops[level.level] ? 0 + level.dropPercentage = if level.started > 0 then level.dropped / level.started * 100 else 0.0 + + sortedLevels = _.cloneDeep @campaignCompletions.levels + sortedLevels = _.filter sortedLevels, ((a) -> a.dropPercentage > 0), @ + if sortedLevels.length >= 3 + sortedLevels.sort (a, b) -> b.dropPercentage - a.dropPercentage + @campaignCompletions.top3DropPercentage = _.pluck sortedLevels[0..2], 'level' + doneCallback() + + return doneCallback() unless @campaignCompletions?.levels? + levelSlugs = _.pluck @campaignCompletions.levels, 'level' + + request = @supermodel.addRequestResource 'level_drops', { + url: '/db/analytics_perday/-/level_drops' + data: {startDay: startDay, endDay: endDay, slugs: levelSlugs} + method: 'POST' + success: success + }, 0 + request.load() + + getCampaignLevelSubscriptions: (startDay, endDay, doneCallback) => + # Fetch level subscriptions + # Needs date format yyyymmdd + success = (data) => + return doneCallback() if @destroyed + # console.log 'getCampaignLevelSubscriptions success', data + levelSubs = {} + for item in data + levelSubs[item.level] = shown: item.shown, purchased: item.purchased + for level in @campaignCompletions.levels + level.subsShown = levelSubs[level.level]?.shown + level.subsPurchased = levelSubs[level.level]?.purchased + doneCallback() + + return doneCallback() unless @campaignCompletions?.levels? + levelSlugs = _.pluck @campaignCompletions.levels, 'level' + + request = @supermodel.addRequestResource 'campaign_subscriptions', { + url: '/db/analytics_perday/-/level_subscriptions' + data: {startDay: startDay, endDay: endDay, slugs: levelSlugs} + method: 'POST' + success: success + }, 0 + request.load() diff --git a/app/views/editor/campaign/CampaignEditorView.coffee b/app/views/editor/campaign/CampaignEditorView.coffee new file mode 100644 index 000000000..4fa466830 --- /dev/null +++ b/app/views/editor/campaign/CampaignEditorView.coffee @@ -0,0 +1,365 @@ +RootView = require 'views/core/RootView' +Campaign = require 'models/Campaign' +Level = require 'models/Level' +Achievement = require 'models/Achievement' +ThangType = require 'models/ThangType' +CampaignView = require 'views/play/CampaignView' +CocoCollection = require 'collections/CocoCollection' +treemaExt = require 'core/treema-ext' +utils = require 'core/utils' +RelatedAchievementsCollection = require 'collections/RelatedAchievementsCollection' +CampaignAnalyticsModal = require './CampaignAnalyticsModal' +CampaignLevelView = require './CampaignLevelView' +SaveCampaignModal = require './SaveCampaignModal' +PatchesView = require 'views/editor/PatchesView' + +achievementProject = ['related', 'rewards', 'name', 'slug'] +thangTypeProject = ['name', 'original'] + +module.exports = class CampaignEditorView extends RootView + id: "campaign-editor-view" + template: require 'templates/editor/campaign/campaign-editor-view' + className: 'editor' + + events: + 'click #analytics-button': 'onClickAnalyticsButton' + 'click #save-button': 'onClickSaveButton' + 'click #patches-button': 'onClickPatches' + + subscriptions: + 'editor:campaign-analytics-modal-closed' : 'onAnalyticsModalClosed' + + constructor: (options, @campaignHandle) -> + super(options) + @campaign = new Campaign({_id:@campaignHandle}) + @supermodel.loadModel(@campaign, 'campaign') + @listenToOnce @campaign, 'sync', (model, response, jqXHR) -> + @campaign.set '_id', response._id + @campaign.url = -> '/db/campaign/' + @id + + # Save reference to data used by anlytics modal so it persists across modal open/closes. + @campaignAnalytics = {} + + @levels = new CocoCollection([], { + model: Level + url: "/db/campaign/#{@campaignHandle}/levels" + project: Campaign.denormalizedLevelProperties + }) + @supermodel.loadCollection(@levels, 'levels') + + @achievements = new CocoCollection([], { + model: Achievement + url: "/db/campaign/#{@campaignHandle}/achievements" + project: achievementProject + }) + @supermodel.loadCollection(@achievements, 'achievements') + + @toSave = new Backbone.Collection() + @listenToOnce @campaign ,'sync', @loadThangTypeNames + @listenToOnce @campaign, 'sync', @onFundamentalLoaded + @listenToOnce @levels, 'sync', @onFundamentalLoaded + @listenToOnce @achievements, 'sync', @onFundamentalLoaded + + loadThangTypeNames: -> + # Load the names of the ThangTypes that this level's Treema nodes might want to display. + originals = [] + for level in _.values(@campaign.get('levels')) + originals = originals.concat(_.values(level.requiredGear)) if level.requiredGear + originals = originals.concat(_.values(level.restrictedGear)) if level.restrictedGear + originals = originals.concat(level.allowedHeroes) if level.allowedHeroes + originals = _.uniq _.flatten originals + for original in originals + thangType = new ThangType() + thangType.setProjection(thangTypeProject) + thangType.setURL("/db/thang.type/#{original}/version") + @supermodel.loadModel(thangType, 'thang') + + onFundamentalLoaded: -> + # Load any levels which haven't been denormalized into our campaign. + return unless @campaign.loaded and @levels.loaded and @achievements.loaded + for level in _.values(@campaign.get('levels')) + continue if model = @levels.findWhere(original: level.original) + model = new Level({}) + model.setProjection Campaign.denormalizedLevelProperties + model.setURL("/db/level/#{level.original}/version") + @levels.add @supermodel.loadModel(model, 'level').model + achievements = new RelatedAchievementsCollection level.original + achievements.setProjection achievementProject + @supermodel.loadCollection achievements, 'achievements' + @listenToOnce achievements, 'sync', -> + @achievements.add(achievements.models) + + onLoaded: -> + @toSave.add @campaign if @campaign.hasLocalChanges() + campaignLevels = $.extend({}, @campaign.get('levels')) + for level in @levels.models + levelOriginal = level.get('original') + campaignLevel = campaignLevels[levelOriginal] + continue if not campaignLevel + + $.extend campaignLevel, _.omit(level.attributes, '_id') + achievements = @achievements.where {'related': levelOriginal} + rewards = [] + for achievement in achievements + for rewardType, rewardArray of achievement.get('rewards') + for reward in rewardArray + rewardObject = { achievement: achievement.id } + + if rewardType is 'heroes' + rewardObject.hero = reward + thangType = new ThangType({}, {project: thangTypeProject}) + thangType.setURL("/db/thang.type/#{reward}/version") + @supermodel.loadModel(thangType, 'thang') + + if rewardType is 'levels' + rewardObject.level = reward + if not @levels.findWhere({original: reward}) + level = new Level({}, {project: Campaign.denormalizedLevelProperties}) + level.setURL("/db/level/#{reward}/version") + @supermodel.loadModel(level, 'level') + + if rewardType is 'items' + rewardObject.item = reward + thangType = new ThangType({}, {project: thangTypeProject}) + thangType.setURL("/db/thang.type/#{reward}/version") + @supermodel.loadModel(thangType, 'thang') + + rewards.push rewardObject + campaignLevel.rewards = rewards + delete campaignLevel.unlocks + campaignLevel.campaign = @campaign.get 'slug' + campaignLevels[levelOriginal] = campaignLevel + + @campaign.set('levels', campaignLevels) + + for level in _.values campaignLevels + model = @levels.findWhere {original: level.original} + model.set key, level[key] for key in Campaign.denormalizedLevelProperties + @toSave.add model if model.hasLocalChanges() + @updateRewardsForLevel model, level.rewards + + super() + + getRenderData: -> + c = super() + c.campaign = @campaign + c + + onClickPatches: (e) -> + @patchesView = @insertSubView(new PatchesView(@campaign), @$el.find('.patches-view')) + @patchesView.load() + @patchesView.$el.removeClass 'hidden' + + onClickAnalyticsButton: -> + @openModalView new CampaignAnalyticsModal {}, @campaignHandle, @campaignAnalytics + + onAnalyticsModalClosed: (options) -> + if options.targetLevelSlug? and @treema.childrenTreemas?.levels?.childrenTreemas? + for original, level of @treema.childrenTreemas.levels.childrenTreemas + if level.data?.slug is options.targetLevelSlug + @openCampaignLevelView @supermodel.getModelByOriginal Level, original + break + + onClickSaveButton: -> + @toSave.set @toSave.filter (m) -> m.hasLocalChanges() + @openModalView new SaveCampaignModal({}, @toSave) + + afterRender: -> + super() + treemaOptions = + schema: Campaign.schema + data: $.extend({}, @campaign.attributes) + filePath: "db/campaign/#{@campaign.get('_id')}" + callbacks: + change: @onTreemaChanged + select: @onTreemaSelectionChanged + dblclick: @onTreemaDoubleClicked + nodeClasses: + levels: LevelsNode + level: LevelNode + campaigns: CampaignsNode + campaign: CampaignNode + achievement: AchievementNode + supermodel: @supermodel + + @treema = @$el.find('#campaign-treema').treema treemaOptions + @treema.build() + @treema.open() + @treema.childrenTreemas.levels?.open() + + @campaignView = new CampaignView({editorMode: true, supermodel: @supermodel}, @campaignHandle) + @campaignView.highlightElement = _.noop # make it stop + @listenTo @campaignView, 'level-moved', @onCampaignLevelMoved + @listenTo @campaignView, 'adjacent-campaign-moved', @onAdjacentCampaignMoved + @listenTo @campaignView, 'level-clicked', @onCampaignLevelClicked + @listenTo @campaignView, 'level-double-clicked', @onCampaignLevelDoubleClicked + @listenTo @campaign, 'change:i18n', => + @campaign.updateI18NCoverage() + @treema.set('/i18n', @campaign.get('i18n')) + @treema.set('/i18nCoverage', @campaign.get('i18nCoverage')) + + @insertSubView @campaignView + + onTreemaChanged: (e, nodes) => + for node in nodes + path = node.getPath() + if _.string.startsWith path, '/levels/' + parts = path.split('/') + original = parts[2] + level = @supermodel.getModelByOriginal Level, original + campaignLevel = @treema.get "/levels/#{original}" + + @updateRewardsForLevel level, campaignLevel.rewards + + level.set key, campaignLevel[key] for key in Campaign.denormalizedLevelProperties + @toSave.add level if level.hasLocalChanges() + + @toSave.add @campaign + @campaign.set key, value for key, value of @treema.data + @campaignView.setCampaign(@campaign) + + onTreemaDoubleClicked: (e, node) => + path = node.getPath() + return unless _.string.startsWith path, '/levels/' + original = path.split('/')[2] + @openCampaignLevelView @supermodel.getModelByOriginal Level, original + + onCampaignLevelMoved: (e) -> + path = "levels/#{e.levelOriginal}/position" + @treema.set path, e.position + + onAdjacentCampaignMoved: (e) -> + path = "adjacentCampaigns/#{e.campaignID}/position" + @treema.set path, e.position + + onCampaignLevelClicked: (levelOriginal) -> + return unless levelTreema = @treema.childrenTreemas?.levels?.childrenTreemas?[levelOriginal] + if key.ctrl or key.command + url = "/editor/level/#{levelTreema.data.slug}" + window.open url, '_blank' + levelTreema.select() + #levelTreema.open() + + onCampaignLevelDoubleClicked: (levelOriginal) -> + @openCampaignLevelView @supermodel.getModelByOriginal Level, levelOriginal + + openCampaignLevelView: (level) -> + @insertSubView campaignLevelView = new CampaignLevelView({}, level) + @listenToOnce campaignLevelView, 'hidden', => @$el.find('#campaign-view').show() + @$el.find('#campaign-view').hide() + + updateRewardsForLevel: (level, rewards) -> + achievements = @supermodel.getModels(Achievement) + achievements = (a for a in achievements when a.get('related') is level.get('original')) + for achievement in achievements + rewardSubset = (r for r in rewards when r.achievement is achievement.id) + oldRewards = achievement.get 'rewards' + newRewards = {} + + heroes = _.compact((r.hero for r in rewardSubset)) + newRewards.heroes = heroes if heroes.length + + items = _.compact((r.item for r in rewardSubset)) + newRewards.items = items if items.length + + levels = _.compact((r.level for r in rewardSubset)) + newRewards.levels = levels if levels.length + + newRewards.gems = oldRewards.gems if oldRewards.gems + achievement.set 'rewards', newRewards + if achievement.hasLocalChanges() + @toSave.add achievement + + onClickLoginButton: -> + # Do Nothing + # This is a override method to RootView, so that only CampaignView is listenting to login button click + + onClickSignupButton: -> + # Do Nothing + # This is a override method to RootView, so that only CampaignView is listenting to signup button click + +class LevelsNode extends TreemaObjectNode + valueClass: 'treema-levels' + @levels: {} + ordered: true + + buildValueForDisplay: (valEl, data) -> + @buildValueForDisplaySimply valEl, ''+_.size(data) + + childPropertiesAvailable: -> @childSource + + childSource: (req, res) => + s = new Backbone.Collection([], {model:Level}) + s.url = '/db/level' + s.fetch({data: {term:req.term, project: Campaign.denormalizedLevelProperties.join(',')}}) + s.once 'sync', (collection) => + for level in collection.models + LevelsNode.levels[level.get('original')] = level + @settings.supermodel.registerModel level + mapped = ({label: r.get('name'), value: r.get('original')} for r in collection.models) + res(mapped) + + +class LevelNode extends TreemaObjectNode + valueClass: 'treema-level' + buildValueForDisplay: (valEl, data) -> + name = data.name + if data.requiresSubscription + name = "[P] " + name + + status = '' + el = 'strong' + if data.adminOnly + status += " (disabled)" + el = 'span' + else if data.adventurer + status += " (adventurer)" + + completion = '' + if data.tasks + completion = "#{(t for t in data.tasks when t.complete).length} / #{data.tasks.length}" + + valEl.append $("<a href='/editor/level/#{_.string.slugify(data.name)}' class='spr'>(e)</a>") + valEl.append $("<#{el}></#{el}>").addClass('treema-shortened').text name + if status + valEl.append $('<em class="spl"></em>').text status + if completion + valEl.append $('<span class="completion"></span>').text completion + + populateData: -> + return if @data.name? + data = _.pick LevelsNode.levels[@keyForParent].attributes, Campaign.denormalizedLevelProperties + _.extend @data, data + +class CampaignsNode extends TreemaObjectNode + valueClass: 'treema-campaigns' + @campaigns: {} + + buildValueForDisplay: (valEl, data) -> + @buildValueForDisplaySimply valEl, ''+_.size(data) + + childPropertiesAvailable: -> @childSource + + childSource: (req, res) => + s = new Backbone.Collection([], {model:Campaign}) + s.url = '/db/campaign' + s.fetch({data: {term:req.term, project: Campaign.denormalizedCampaignProperties}}) + s.once 'sync', (collection) -> + CampaignsNode.campaigns[campaign.id] = campaign for campaign in collection.models + mapped = ({label: r.get('name'), value: r.id} for r in collection.models) + res(mapped) + + +class CampaignNode extends TreemaObjectNode + valueClass: 'treema-campaign' + buildValueForDisplay: (valEl, data) -> + @buildValueForDisplaySimply valEl, data.name + + populateData: -> + return if @data.name? + # TODO: Need to be able to update i18n links to other campaigns + data = _.pick CampaignsNode.campaigns[@keyForParent].attributes, Campaign.denormalizedCampaignProperties + _.extend @data, data + +class AchievementNode extends treemaExt.IDReferenceNode + buildSearchURL: (term) -> "#{@url}?term=#{term}&project=#{achievementProject.join(',')}" diff --git a/app/views/editor/campaign/CampaignLevelView.coffee b/app/views/editor/campaign/CampaignLevelView.coffee new file mode 100644 index 000000000..3864a7686 --- /dev/null +++ b/app/views/editor/campaign/CampaignLevelView.coffee @@ -0,0 +1,472 @@ +CocoView = require 'views/core/CocoView' +Level = require 'models/Level' +LevelSession = require 'models/LevelSession' +ModelModal = require 'views/modal/ModelModal' +User = require 'models/User' +utils = require 'core/utils' + +module.exports = class CampaignLevelView extends CocoView + id: 'campaign-level-view' + template: require 'templates/editor/campaign/campaign-level-view' + + events: + 'change .line-graph-checkbox': 'updateGraphCheckbox' + 'click .close': 'onClickClose' + 'click #reload-button': 'onClickReloadButton' + 'dblclick .recent-session': 'onDblClickRecentSession' + 'mouseenter .graph-point': 'onMouseEnterPoint' + 'mouseleave .graph-point': 'onMouseLeavePoint' + 'click .replay-button': 'onClickReplay' + + constructor: (options, @level) -> + super(options) + @fullLevel = new Level _id: @level.id + @fullLevel.fetch() + @listenToOnce @fullLevel, 'sync', => @render?() + @levelSlug = @level.get('slug') + @getAnalytics() + + getRenderData: -> + c = super() + c.level = if @fullLevel.loaded then @fullLevel else @level + c.analytics = @analytics + c + + afterRender: -> + super() + $("#input-startday").datepicker dateFormat: "yy-mm-dd" + $("#input-endday").datepicker dateFormat: "yy-mm-dd" + # TODO: Why does this have to be called from afterRender() instead of getRenderData()? + @updateAnalyticsGraphs() + + updateGraphCheckbox: (e) -> + lineID = $(e.target).data('lineid') + checked = $(e.target).prop('checked') + for graph in @analytics.graphs + for line in graph.lines + if line.lineID is lineID + line.enabled = checked + return @render() + + onClickClose: -> + @$el.addClass('hidden') + @trigger 'hidden' + + onClickReloadButton: () => + startDay = $('#input-startday').val() + endDay = $('#input-endday').val() + @getAnalytics startDay, endDay + + onDblClickRecentSession: (e) -> + # Admin view of players' code + return unless me.isAdmin() + row = $(e.target).parent() + player = new User _id: row.data 'player-id' + session = new LevelSession _id: row.data 'session-id' + @openModalView new ModelModal models: [session, player] + + onMouseEnterPoint: (e) -> + pointID = $(e.target).data('pointid') + container = @$el.find(".graph-point-info-container[data-pointid=#{pointID}]").show() + margin = 20 + width = container.outerWidth() + height = container.outerHeight() + container.css('left', e.offsetX - width / 2) + container.css('top', e.offsetY - height - margin) + + onMouseLeavePoint: (e) -> + pointID = $(e.target).data('pointid') + @$el.find(".graph-point-info-container[data-pointid=#{pointID}]").hide() + + onClickReplay: (e) -> + sessionID = $(e.target).closest('tr').data 'session-id' + url = "/play/level/#{@level.get('slug')}?session=#{sessionID}&observing=true" + window.open url, '_blank' + + updateAnalyticsGraphData: -> + # console.log 'updateAnalyticsGraphData' + # Build graphs based on available @analytics data + # Currently only one graph + @analytics.graphs = [graphID: 'level-completions', lines: []] + + # TODO: Where should this metadata live? + # TODO: lineIDs assumed to be unique across graphs + completionLineID = 'level-completions' + playtimeLineID = 'level-playtime' + helpsLineID = 'helps-clicked' + videosLineID = 'help-videos' + lineMetadata = {} + lineMetadata[completionLineID] = + description: 'Level Completion (%)' + color: 'red' + lineMetadata[playtimeLineID] = + description: 'Average Playtime (s)' + color: 'green' + lineMetadata[helpsLineID] = + description: 'Help click rate (%)' + color: 'blue' + lineMetadata[videosLineID] = + description: 'Help video rate (%)' + color: 'purple' + + # Use this days aggregate to fill in missing days from the analytics data + days = {} + days["#{day.created[0..3]}-#{day.created[4..5]}-#{day.created[6..7]}"] = true for day in @analytics.levelCompletions.data if @analytics?.levelCompletions?.data? + days[day.created] = true for day in @analytics.levelPlaytimes.data if @analytics?.levelPlaytimes?.data? + days["#{day.day[0..3]}-#{day.day[4..5]}-#{day.day[6..7]}"] = true for day in @analytics.levelHelps.data if @analytics?.levelHelps?.data? + days = Object.keys(days).sort (a, b) -> if a < b then -1 else 1 + if days.length > 0 + currentIndex = 0 + currentDay = days[currentIndex] + currentDate = new Date(currentDay + "T00:00:00.000Z") + lastDay = days[days.length - 1] + while currentDay isnt lastDay + days.splice currentIndex, 0, currentDay if days[currentIndex] isnt currentDay + currentIndex++ + currentDate.setUTCDate(currentDate.getUTCDate() + 1) + currentDay = currentDate.toISOString().substr(0, 10) + + # Update level completion graph data + dayStartedMap = {} + if @analytics?.levelCompletions?.data?.length > 0 + # Build line data + levelPoints = [] + for day, i in @analytics.levelCompletions.data + dayStartedMap[day.created] = day.started + rate = parseFloat(day.rate) + levelPoints.push + x: i + y: rate + started: day.started + day: "#{day.created[0..3]}-#{day.created[4..5]}-#{day.created[6..7]}" + pointID: "#{completionLineID}#{i}" + values: ["Started: #{day.started}", "Finished: #{day.finished}", "Completion rate: #{rate.toFixed(2)}%"] + # Ensure points for each day + for day, i in days + if levelPoints.length <= i or levelPoints[i].day isnt day + levelPoints.splice i, 0, + y: 0.0 + day: day + values: [] + levelPoints[i].x = i + levelPoints[i].pointID = "#{completionLineID}#{i}" + @analytics.graphs[0].lines.push + lineID: completionLineID + enabled: true + points: levelPoints + description: lineMetadata[completionLineID].description + lineColor: lineMetadata[completionLineID].color + min: 0 + max: 100.0 + + # Update average playtime graph data + if @analytics?.levelPlaytimes?.data?.length > 0 + # Build line data + playtimePoints = [] + for day, i in @analytics.levelPlaytimes.data + avg = parseFloat(day.average) + playtimePoints.push + x: i + y: avg + day: day.created + pointID: "#{playtimeLineID}#{i}" + values: ["Average playtime: #{avg.toFixed(2)}s"] + # Ensure points for each day + for day, i in days + if playtimePoints.length <= i or playtimePoints[i].day isnt day + playtimePoints.splice i, 0, + y: 0.0 + day: day + values: [] + playtimePoints[i].x = i + playtimePoints[i].pointID = "#{playtimeLineID}#{i}" + @analytics.graphs[0].lines.push + lineID: playtimeLineID + enabled: true + points: playtimePoints + description: lineMetadata[playtimeLineID].description + lineColor: lineMetadata[playtimeLineID].color + min: 0 + max: d3.max(playtimePoints, (d) -> d.y) + + # Update help graph data + if @analytics?.levelHelps?.data?.length > 0 + # Build line data + helpPoints = [] + videoPoints = [] + for day, i in @analytics.levelHelps.data + helpCount = day.alertHelps + day.paletteHelps + started = dayStartedMap[day.day] ? 0 + clickRate = if started > 0 then helpCount / started * 100 else 0 + videoRate = day.videoStarts / helpCount * 100 + helpPoints.push + x: i + y: clickRate + day: "#{day.day[0..3]}-#{day.day[4..5]}-#{day.day[6..7]}" + pointID: "#{helpsLineID}#{i}" + values: ["Helps clicked: #{helpCount}", "Helps click clickRate: #{clickRate.toFixed(2)}%"] + videoPoints.push + x: i + y: videoRate + day: "#{day.day[0..3]}-#{day.day[4..5]}-#{day.day[6..7]}" + pointID: "#{videosLineID}#{i}" + values: ["Help videos started: #{day.videoStarts}", "Help videos start rate: #{videoRate.toFixed(2)}%"] + # Ensure points for each day + for day, i in days + if helpPoints.length <= i or helpPoints[i].day isnt day + helpPoints.splice i, 0, + y: 0.0 + day: day + values: [] + helpPoints[i].x = i + helpPoints[i].pointID = "#{helpsLineID}#{i}" + if videoPoints.length <= i or videoPoints[i].day isnt day + videoPoints.splice i, 0, + y: 0.0 + day: day + values: [] + videoPoints[i].x = i + videoPoints[i].pointID = "#{videosLineID}#{i}" + if d3.max(helpPoints, (d) -> d.y) > 0 + @analytics.graphs[0].lines.push + lineID: helpsLineID + enabled: true + points: helpPoints + description: lineMetadata[helpsLineID].description + lineColor: lineMetadata[helpsLineID].color + min: 0 + max: 100.0 + if d3.max(videoPoints, (d) -> d.y) > 0 + @analytics.graphs[0].lines.push + lineID: videosLineID + enabled: true + points: videoPoints + description: lineMetadata[videosLineID].description + lineColor: lineMetadata[videosLineID].color + min: 0 + max: 100.0 + + updateAnalyticsGraphs: -> + # Build d3 graphs + return unless @analytics?.graphs?.length > 0 + containerSelector = '.line-graph-container' + # console.log 'updateAnalyticsGraphs', containerSelector, @analytics.graphs + + margin = 20 + keyHeight = 20 + xAxisHeight = 20 + yAxisWidth = 40 + containerWidth = $(containerSelector).width() + containerHeight = $(containerSelector).height() + + for graph in @analytics.graphs + graphLineCount = _.reduce graph.lines, ((sum, item) -> if item.enabled then sum + 1 else sum), 0 + svg = d3.select(containerSelector).append("svg") + .attr("width", containerWidth) + .attr("height", containerHeight) + width = containerWidth - margin * 2 - yAxisWidth * graphLineCount + height = containerHeight - margin * 2 - xAxisHeight - keyHeight * graphLineCount + currentLine = 0 + for line in graph.lines + continue unless line.enabled + xRange = d3.scale.linear().range([0, width]).domain([d3.min(line.points, (d) -> d.x), d3.max(line.points, (d) -> d.x)]) + yRange = d3.scale.linear().range([height, 0]).domain([line.min, line.max]) + + # x-Axis and guideline once + if currentLine is 0 + startDay = new Date(line.points[0].day) + endDay = new Date(line.points[line.points.length - 1].day) + xAxisRange = d3.time.scale() + .domain([startDay, endDay]) + .range([0, width]) + xAxis = d3.svg.axis() + .scale(xAxisRange) + svg.append("g") + .attr("class", "x axis") + .call(xAxis) + .selectAll("text") + .attr("dy", ".35em") + .attr("transform", "translate(" + (margin + yAxisWidth * (graphLineCount - 1)) + "," + (height + margin) + ")") + .style("text-anchor", "start") + + # Horizontal guidelines + svg.selectAll(".line") + .data([10, 30, 50, 70, 90]) + .enter() + .append("line") + .attr("x1", margin + yAxisWidth * graphLineCount) + .attr("y1", (d) -> margin + yRange(d)) + .attr("x2", margin + yAxisWidth * graphLineCount + width) + .attr("y2", (d) -> margin + yRange(d)) + .attr("stroke", line.lineColor) + .style("opacity", "0.5") + + # y-Axis + yAxisRange = d3.scale.linear().range([height, 0]).domain([line.min, line.max]) + yAxis = d3.svg.axis() + .scale(yRange) + .orient("left") + svg.append("g") + .attr("class", "y axis") + .attr("transform", "translate(" + (margin + yAxisWidth * currentLine) + "," + margin + ")") + .style("color", line.lineColor) + .call(yAxis) + .selectAll("text") + .attr("y", 0) + .attr("x", 0) + .attr("fill", line.lineColor) + .style("text-anchor", "start") + + # Key + svg.append("line") + .attr("x1", margin) + .attr("y1", margin + height + xAxisHeight + keyHeight * currentLine + keyHeight / 2) + .attr("x2", margin + 40) + .attr("y2", margin + height + xAxisHeight + keyHeight * currentLine + keyHeight / 2) + .attr("stroke", line.lineColor) + .attr("class", "key-line") + svg.append("text") + .attr("x", margin + 40 + 10) + .attr("y", margin + height + xAxisHeight + keyHeight * currentLine + (keyHeight + 10) / 2) + .attr("fill", line.lineColor) + .attr("class", "key-text") + .text(line.description) + + # Path and points + svg.selectAll(".circle") + .data(line.points) + .enter() + .append("circle") + .attr("transform", "translate(" + (margin + yAxisWidth * graphLineCount) + "," + margin + ")") + .attr("cx", (d) -> xRange(d.x)) + .attr("cy", (d) -> yRange(d.y)) + .attr("r", (d) -> if d.started then Math.max(3, Math.min(10, Math.log(parseInt(d.started)))) + 2 else 6) + .attr("fill", line.lineColor) + .attr("stroke-width", 1) + .attr("class", "graph-point") + .attr("data-pointid", (d) -> "#{line.lineID}#{d.x}") + d3line = d3.svg.line() + .x((d) -> xRange(d.x)) + .y((d) -> yRange(d.y)) + .interpolate("linear") + svg.append("path") + .attr("d", d3line(line.points)) + .attr("transform", "translate(" + (margin + yAxisWidth * graphLineCount) + "," + margin + ")") + .style("stroke-width", 1) + .style("stroke", line.lineColor) + .style("fill", "none") + currentLine++ + + getAnalytics: (startDay, endDay) => + # Analytics APIs use 2 different day formats + if startDay? + startDayDashed = startDay + startDay = startDay.replace(/-/g, '') + else + startDay = utils.getUTCDay -14 + startDayDashed = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}" + if endDay? + endDayDashed = endDay + endDay = endDay.replace(/-/g, '') + else + endDay = utils.getUTCDay -1 + endDayDashed = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}" + + # Initialize + @analytics = + startDay: startDayDashed + endDay: endDayDashed + commonProblems: {data: [], loading: true} + levelCompletions: {data: [], loading: true} + levelHelps: {data: [], loading: true} + levelPlaytimes: {data: [], loading: true} + recentSessions: {data: [], loading: true} + graphs: [] + @render() # Hide old analytics data while we fetch new data + + makeFinishDataFetch = (data) => + return => + return if @destroyed + @updateAnalyticsGraphData() + data.loading = false + @render() + @getCommonLevelProblems startDayDashed, endDayDashed, makeFinishDataFetch(@analytics.commonProblems) + @getLevelCompletions startDay, endDay, makeFinishDataFetch(@analytics.levelCompletions) + @getLevelHelps startDay, endDay, makeFinishDataFetch(@analytics.levelHelps) + @getLevelPlaytimes startDayDashed, endDayDashed, makeFinishDataFetch(@analytics.levelPlaytimes) + @getRecentSessions makeFinishDataFetch(@analytics.recentSessions) + + getCommonLevelProblems: (startDay, endDay, doneCallback) -> + success = (data) => + return doneCallback() if @destroyed + # console.log 'getCommonLevelProblems', data + @analytics.commonProblems.data = data + doneCallback() + request = @supermodel.addRequestResource 'common_problems', { + url: '/db/user_code_problem/-/common_problems' + data: {startDay: startDay, endDay: endDay, slug: @levelSlug} + method: 'POST' + success: success + }, 0 + request.load() + + getLevelCompletions: (startDay, endDay, doneCallback) -> + success = (data) => + return doneCallback() if @destroyed + # console.log 'getLevelCompletions', data + data.sort (a, b) -> if a.created < b.created then -1 else 1 + mapFn = (item) -> + item.rate = if item.started > 0 then item.finished / item.started * 100 else 0 + item + @analytics.levelCompletions.data = _.map data, mapFn, @ + doneCallback() + request = @supermodel.addRequestResource 'level_completions', { + url: '/db/analytics_perday/-/level_completions' + data: {startDay: startDay, endDay: endDay, slug: @levelSlug} + method: 'POST' + success: success + }, 0 + request.load() + + getLevelHelps: (startDay, endDay, doneCallback) -> + success = (data) => + return doneCallback() if @destroyed + # console.log 'getLevelHelps', data + @analytics.levelHelps.data = data.sort (a, b) -> if a.day < b.day then -1 else 1 + doneCallback() + request = @supermodel.addRequestResource 'level_helps', { + url: '/db/analytics_perday/-/level_helps' + data: {startDay: startDay, endDay: endDay, slugs: [@levelSlug]} + method: 'POST' + success: success + }, 0 + request.load() + + getLevelPlaytimes: (startDay, endDay, doneCallback) -> + success = (data) => + return doneCallback() if @destroyed + # console.log 'getLevelPlaytimes', data + @analytics.levelPlaytimes.data = data.sort (a, b) -> if a.created < b.created then -1 else 1 + doneCallback() + request = @supermodel.addRequestResource 'playtime_averages', { + url: '/db/level/-/playtime_averages' + data: {startDay: startDay, endDay: endDay, slugs: [@levelSlug]} + method: 'POST' + success: success + }, 0 + request.load() + + getRecentSessions: (doneCallback) -> + limit = 100 + success = (data) => + return doneCallback() if @destroyed + # console.log 'getRecentSessions', data + @analytics.recentSessions.data = data + doneCallback() + request = @supermodel.addRequestResource 'level_sessions_recent', { + url: "/db/level_session/-/recent" + data: {slug: @levelSlug, limit: limit} + method: 'POST' + success: success + }, 0 + request.load() diff --git a/app/views/editor/campaign/SaveCampaignModal.coffee b/app/views/editor/campaign/SaveCampaignModal.coffee new file mode 100644 index 000000000..e4a5a23d7 --- /dev/null +++ b/app/views/editor/campaign/SaveCampaignModal.coffee @@ -0,0 +1,46 @@ +ModalView = require 'views/core/ModalView' +template = require 'templates/editor/campaign/save-campaign-modal' +DeltaView = require 'views/editor/DeltaView' + +module.exports = class SaveCampaignModal extends ModalView + id: 'save-campaign-modal' + template: template + plain: true + + events: + 'click #save-button': 'onClickSaveButton' + + constructor: (options, @modelsToSave) -> + super(options) + + getRenderData: -> + c = super() + c.modelsToSave = @modelsToSave + c + + afterRender: -> + @$el.find('.delta-view').each((i, el) => + $el = $(el) + model = @modelsToSave.find( id: $el.data('model-id')) + deltaView = new DeltaView({model: model}) + @insertSubView(deltaView, $el) + ) + super() + + onClickSaveButton: -> + @showLoading() + @reverseLevelsBeforeSave() + modelsBeingSaved = (model.patch() for model in @modelsToSave.models) + $.when(_.compact(modelsBeingSaved)...).done(-> document.location.reload()) + + reverseLevelsBeforeSave: -> + # For some unfathomable reason, not in our code anywhere, the levels get reversed during the save somehow. + # Since we want to maintain their orders, we reverse them first, so that when they're reversed again, it's right. + # Yaaaay! + return unless campaign = _.find @modelsToSave.models, (m) -> m.constructor.className is 'Campaign' + levelsReversed = {} + levels = campaign.get 'levels' + levelIDs = _.keys(levels).reverse() + for levelID in levelIDs + levelsReversed[levelID] = levels[levelID] + campaign.set 'levels', levelsReversed diff --git a/app/views/editor/component/ComponentVersionsModal.coffee b/app/views/editor/component/ComponentVersionsModal.coffee index fb027826e..3de2ad4b6 100755 --- a/app/views/editor/component/ComponentVersionsModal.coffee +++ b/app/views/editor/component/ComponentVersionsModal.coffee @@ -1,4 +1,4 @@ -VersionsModal = require 'views/modal/VersionsModal' +VersionsModal = require 'views/editor/modal/VersionsModal' module.exports = class ComponentVersionsModal extends VersionsModal id: 'editor-component-versions-view' @@ -6,4 +6,4 @@ module.exports = class ComponentVersionsModal extends VersionsModal page: 'component' constructor: (options, @ID) -> - super options, ID, require 'models/LevelComponent' + super options, @ID, require 'models/LevelComponent' diff --git a/app/views/editor/component/ThangComponentConfigView.coffee b/app/views/editor/component/ThangComponentConfigView.coffee index 2d7a90957..5b624d2a5 100644 --- a/app/views/editor/component/ThangComponentConfigView.coffee +++ b/app/views/editor/component/ThangComponentConfigView.coffee @@ -4,6 +4,7 @@ template = require 'templates/editor/component/thang-component-config-view' Level = require 'models/Level' LevelComponent = require 'models/LevelComponent' nodes = require '../level/treema_nodes' +require 'vendor/treema' module.exports = class ThangComponentConfigView extends CocoView className: 'thang-component-config-view' diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index 86fbf624e..630b46e2e 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -8,14 +8,15 @@ ComponentsCollection = require 'collections/ComponentsCollection' ThangComponentConfigView = require './ThangComponentConfigView' AddThangComponentsModal = require './AddThangComponentsModal' nodes = require '../level/treema_nodes' +require 'vendor/treema' ThangType = require 'models/ThangType' CocoCollection = require 'collections/CocoCollection' LC = (componentName, config) -> original: LevelComponent[componentName + 'ID'], majorVersion: 0, config: config DEFAULT_COMPONENTS = - Unit: [LC('Equips')] - Hero: [LC('Equips')] + Unit: [LC('Equips'), LC('FindsPaths')] + Hero: [LC('Equips'), LC('FindsPaths')] Floor: [ LC('Exists', stateless: true) LC('Physical', width: 20, height: 17, depth: 2, shape: 'sheet', pos: {x: 10, y: 8.5, z: 1}) @@ -34,10 +35,7 @@ DEFAULT_COMPONENTS = Misc: [LC('Exists'), LC('Physical')] Mark: [] Item: [LC('Item')] - -class ItemThangTypeSearchCollection extends CocoCollection - url: '/db/thang.type?view=items&project=original,name,version,slug,kind,components' - model: ThangType + Missile: [LC('Missile')] module.exports = class ThangComponentsEditView extends CocoView id: 'thang-components-edit-view' @@ -345,7 +343,7 @@ module.exports = class ThangComponentsEditView extends CocoView componentSystems = (c.get('system') for c in componentModels when c) for system in componentSystems - if system not in extantSystems + if system isnt 'misc' and system not in extantSystems s = "Component requires system <strong>#{system}</strong> which is currently not included in this level." noty({ text: s, diff --git a/app/views/docs/ComponentsDocumentationView.coffee b/app/views/editor/docs/ComponentsDocumentationView.coffee similarity index 95% rename from app/views/docs/ComponentsDocumentationView.coffee rename to app/views/editor/docs/ComponentsDocumentationView.coffee index 15c2661cd..c74ec3147 100644 --- a/app/views/docs/ComponentsDocumentationView.coffee +++ b/app/views/editor/docs/ComponentsDocumentationView.coffee @@ -1,5 +1,5 @@ CocoView = require 'views/core/CocoView' -template = require 'templates/docs/components-documentation-view' +template = require 'templates/editor/docs/components-documentation-view' CocoCollection = require 'collections/CocoCollection' LevelComponent = require 'models/LevelComponent' diff --git a/app/views/docs/SystemsDocumentationView.coffee b/app/views/editor/docs/SystemsDocumentationView.coffee similarity index 94% rename from app/views/docs/SystemsDocumentationView.coffee rename to app/views/editor/docs/SystemsDocumentationView.coffee index bc0df08ec..4489832fb 100644 --- a/app/views/docs/SystemsDocumentationView.coffee +++ b/app/views/editor/docs/SystemsDocumentationView.coffee @@ -1,5 +1,5 @@ CocoView = require 'views/core/CocoView' -template = require 'templates/docs/systems-documentation-view' +template = require 'templates/editor/docs/systems-documentation-view' CocoCollection = require 'collections/CocoCollection' LevelSystem = require 'models/LevelSystem' diff --git a/app/views/editor/level/LevelEditView.coffee b/app/views/editor/level/LevelEditView.coffee index a50a66bfb..7e53803fa 100644 --- a/app/views/editor/level/LevelEditView.coffee +++ b/app/views/editor/level/LevelEditView.coffee @@ -17,15 +17,26 @@ ComponentsTabView = require './components/ComponentsTabView' SystemsTabView = require './systems/SystemsTabView' SaveLevelModal = require './modals/SaveLevelModal' ForkModal = require 'views/editor/ForkModal' -SaveVersionModal = require 'views/modal/SaveVersionModal' +SaveVersionModal = require 'views/editor/modal/SaveVersionModal' PatchesView = require 'views/editor/PatchesView' RelatedAchievementsView = require 'views/editor/level/RelatedAchievementsView' VersionHistoryView = require './modals/LevelVersionsModal' -ComponentsDocumentationView = require 'views/docs/ComponentsDocumentationView' -SystemsDocumentationView = require 'views/docs/SystemsDocumentationView' +ComponentsDocumentationView = require 'views/editor/docs/ComponentsDocumentationView' +SystemsDocumentationView = require 'views/editor/docs/SystemsDocumentationView' LevelFeedbackView = require 'views/editor/level/LevelFeedbackView' storage = require 'core/storage' +require 'vendor/coffeescript' # this is tenuous, since the LevelSession and LevelComponent models are what compile the code +require 'vendor/treema' + +# Make sure that all of our Aethers are loaded, so that if we try to preview the level, it will work. +require 'vendor/aether-javascript' +require 'vendor/aether-python' +require 'vendor/aether-coffeescript' +require 'vendor/aether-lua' +require 'vendor/aether-clojure' +require 'vendor/aether-io' + module.exports = class LevelEditView extends RootView id: 'editor-level-view' className: 'editor' @@ -37,7 +48,7 @@ module.exports = class LevelEditView extends RootView 'click .play-with-team-button': 'onPlayLevel' 'click .play-with-team-parent': 'onPlayLevelTeamSelect' 'click #commit-level-start-button': 'startCommittingLevel' - 'click #fork-start-button': 'startForking' + 'click li:not(.disabled) > #fork-start-button': 'startForking' 'click #level-history-button': 'showVersionHistory' 'click #undo-button': 'onUndo' 'mouseenter #undo-button': 'showUndoDescription' @@ -47,7 +58,7 @@ module.exports = class LevelEditView extends RootView 'click #components-tab': -> @subviews.editor_level_components_tab_view.refreshLevelThangsTreema @level.get('thangs') 'click #level-patch-button': 'startPatchingLevel' 'click #level-watch-button': 'toggleWatchLevel' - 'click #pop-level-i18n-button': 'onPopulateI18N' + 'click li:not(.disabled) > #pop-level-i18n-button': 'onPopulateI18N' 'click a[href="#editor-level-documentation"]': 'onClickDocumentationTab' 'mouseup .nav-tabs > li a': 'toggleTab' @@ -55,21 +66,26 @@ module.exports = class LevelEditView extends RootView super options @supermodel.shouldSaveBackups = (model) -> model.constructor.className in ['Level', 'LevelComponent', 'LevelSystem', 'ThangType'] - @levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, headless: true + @levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, headless: true, sessionless: true @level = @levelLoader.level @files = new DocumentFiles(@levelLoader.level) @supermodel.loadCollection(@files, 'file_names') + destroy: -> + clearInterval @timerIntervalID + super() + showLoading: ($el) -> $el ?= @$el.find('.outer-content') super($el) - + getTitle: -> "LevelEditor - " + (@level.get('name') or '...') onLoaded: -> _.defer => @world = @levelLoader.world @render() + @timerIntervalID = setInterval @incrementBuildTime, 1000 getRenderData: (context={}) -> context = super(context) @@ -134,11 +150,11 @@ module.exports = class LevelEditView extends RootView showUndoDescription: -> undoDescription = TreemaNode.getLastTreemaWithFocus().getUndoDescription() - @$el.find('#undo-button').attr('title', 'Undo ' + undoDescription + ' (Ctrl+Z)') + @$el.find('#undo-button').attr('title', $.i18n.t("general.undo_prefix") + " " + undoDescription + " " + $.i18n.t("general.undo_shortcut")) showRedoDescription: -> redoDescription = TreemaNode.getLastTreemaWithFocus().getRedoDescription() - @$el.find('#redo-button').attr('title', 'Redo ' + redoDescription + ' (Ctrl+Shift+Z)') + @$el.find('#redo-button').attr('title', $.i18n.t("general.redo_prefix") + " " + redoDescription + " " + $.i18n.t("general.redo_shortcut")) getCurrentView: -> currentViewID = @$el.find('.tab-pane.active').attr('id') @@ -151,7 +167,7 @@ module.exports = class LevelEditView extends RootView Backbone.Mediator.publish 'editor:view-switched', {} startCommittingLevel: (e) -> - @openModalView new SaveLevelModal level: @level, supermodel: @supermodel + @openModalView new SaveLevelModal level: @level, supermodel: @supermodel, buildTime: @levelBuildTime Backbone.Mediator.publish 'editor:view-switched', {} startForking: (e) -> @@ -167,11 +183,11 @@ module.exports = class LevelEditView extends RootView button = @$el.find('#level-watch-button') @level.watch(button.find('.watch').is(':visible')) button.find('> span').toggleClass('secret') - + onPopulateI18N: -> @level.populateI18N() f = -> document.location.reload() - setTimeout(f, 200) + setTimeout(f, 2000) toggleTab: (e) -> @renderScrollbar() @@ -189,3 +205,8 @@ module.exports = class LevelEditView extends RootView return if @initializedDocs @initializedDocs = true @$el.find('a[href="#components-documentation-view"]').click() + + incrementBuildTime: => + return if application.userIsIdle + @levelBuildTime ?= @level.get('buildTime') ? 0 + ++@levelBuildTime diff --git a/app/views/editor/level/components/ComponentsTabView.coffee b/app/views/editor/level/components/ComponentsTabView.coffee index e92e37027..f24850370 100644 --- a/app/views/editor/level/components/ComponentsTabView.coffee +++ b/app/views/editor/level/components/ComponentsTabView.coffee @@ -4,6 +4,7 @@ ThangType = require 'models/ThangType' LevelComponent = require 'models/LevelComponent' LevelComponentEditView = require './LevelComponentEditView' LevelComponentNewView = require './NewLevelComponentModal' +require 'vendor/treema' class LevelComponentCollection extends Backbone.Collection url: '/db/level.component' @@ -30,7 +31,7 @@ module.exports = class ComponentsTabView extends CocoView thangType = @supermodel.getModelByOriginal ThangType, thang.thangType for component in thangType.get('components') ? [] componentMap[component.original] = component - + for component in thang.components componentMap[component.original] = component @@ -44,11 +45,16 @@ module.exports = class ComponentsTabView extends CocoView componentModelMap = {} componentModelMap[comp.get('original')] = comp for comp in componentModels components = ({original: key.split('.')[0], majorVersion: parseInt(key.split('.')[1], 10), thangs: value, count: value.length} for key, value of @presentComponents) - treemaData = _.sortBy components, (comp) -> - comp = componentModelMap[comp.original] - res = [comp.get('system'), comp.get('name')] + components = components.concat ({original: c.get('original'), majorVersion: c.get('version').major, thangs: [], count: 0} for c in componentModels when not @presentComponents[c.get('original') + '.' + c.get('version').major]) + treemaData = _.sortBy components, (comp) => + component = componentModelMap[comp.original] + res = [(if comp.count then 0 else 1), component.get('system'), component.get('name')] return res + res = {} + res[treemaData[key].original] = treemaData[key] for key in [0 ... treemaData.length] + treemaData = (value for key, value of res) # Removing duplicates from treemaData + treemaOptions = supermodel: @supermodel schema: {type: 'array', items: {type: 'object', format: 'level-component'}} @@ -81,7 +87,7 @@ module.exports = class ComponentsTabView extends CocoView onLevelComponentEditingEnded: (e) -> @removeSubView @levelComponentEditView @levelComponentEditView = null - + destroy: -> @componentsTreema?.destroy() super() @@ -97,4 +103,6 @@ class LevelComponentNode extends TreemaObjectNode comp = _.find @settings.supermodel.getModels(LevelComponent), (m) => m.get('original') is data.original and m.get('version').major is data.majorVersion name = "#{comp.get('system')}.#{comp.get('name')} v#{comp.get('version').major}" - @buildValueForDisplaySimply valEl, "#{name} (#{count})" + result = @buildValueForDisplaySimply valEl, "#{name} (#{count})" + result.addClass 'not-present' unless data.count + result diff --git a/app/views/editor/level/components/LevelComponentEditView.coffee b/app/views/editor/level/components/LevelComponentEditView.coffee index 39440d767..65e0672ac 100644 --- a/app/views/editor/level/components/LevelComponentEditView.coffee +++ b/app/views/editor/level/components/LevelComponentEditView.coffee @@ -3,7 +3,8 @@ template = require 'templates/editor/level/component/level-component-edit-view' LevelComponent = require 'models/LevelComponent' ComponentVersionsModal = require 'views/editor/component/ComponentVersionsModal' PatchesView = require 'views/editor/PatchesView' -SaveVersionModal = require 'views/modal/SaveVersionModal' +SaveVersionModal = require 'views/editor/modal/SaveVersionModal' +require 'vendor/treema' module.exports = class LevelComponentEditView extends CocoView id: 'level-component-edit-view' diff --git a/app/views/editor/level/components/NewLevelComponentModal.coffee b/app/views/editor/level/components/NewLevelComponentModal.coffee index 141a45843..85251d743 100644 --- a/app/views/editor/level/components/NewLevelComponentModal.coffee +++ b/app/views/editor/level/components/NewLevelComponentModal.coffee @@ -28,7 +28,7 @@ module.exports = class NewLevelComponentModal extends ModalView component.set 'name', name component.set 'code', component.get('code', true).replace(/AttacksSelf/g, name) component.set 'permissions', [{access: 'owner', target: me.id}] # Private until saved in a published Level - res = component.save() + res = component.save(null, {type: 'POST'}) # Override PUT so we can trigger postFirstVersion logic return unless res @showLoading() diff --git a/app/views/editor/level/modals/GenerateTerrainModal.coffee b/app/views/editor/level/modals/GenerateTerrainModal.coffee index 3c6b8de2e..e44b49bd1 100644 --- a/app/views/editor/level/modals/GenerateTerrainModal.coffee +++ b/app/views/editor/level/modals/GenerateTerrainModal.coffee @@ -87,6 +87,34 @@ clusters = { 'thangs': ['Bookshelf', 'Chair', 'Table', 'Candle', 'Treasure Chest'] 'margin': -1 } + 'desert_walls': { + 'thangs': ['Desert Wall 1', 'Desert Wall 2', 'Desert Wall 3', 'Desert Wall 4', 'Desert Wall 5', 'Desert Wall 6', 'Desert Wall 7', 'Desert Wall 8'] + 'margin': 6 + } + 'desert_floor': { + 'thangs': ['Sand 01', 'Sand 02', 'Sand 03', 'Sand 04', 'Sand 05', 'Sand 06'] + 'margin': -1 + } + 'oases': { + 'thangs': ['Oasis 1', 'Oasis 2', 'Oasis 3'] + 'margin': 4 + } + 'mountain_floor': { + 'thangs': ['Talus 1', 'Talus 2', 'Talus 3', 'Talus 4', 'Talus 5', 'Talus 6'] + 'margin': -1 + } + 'mountain_walls': { + 'thangs': ['Mountain 1','Mountain 3'] + 'margin': 6 + } + 'glacier_floor': { + 'thangs': ['Firn 1', 'Firn 2', 'Firn 3', 'Firn 4', 'Firn 5', 'Firn 6'] + 'margin': -1 + } + 'glacier_walls': { + 'thangs': ['Ice Wall'] + 'margin': 2 + } } presets = { @@ -207,6 +235,80 @@ presets = { } } } + 'desert': { + 'terrainName': 'Desert' + 'type':'desert' + 'borders':'desert_walls' + 'borderNoise':2 + 'borderSize':4 + 'borderThickness':4 + 'floors':'desert_floor' + 'decorations': { + 'hero': { + 'num': [1, 1] + 'width': 2 + 'height': 2 + 'clusters': { + 'hero': [1, 1] + } + } + 'oasis': { + 'num':[1,2] #min-max + 'width': 10 + 'height': 10 + 'clusters': { + 'oases':[1,1] + 'shrubs':[0,5] + 'rocks':[0,2] + } + } + } + }, + 'mountain': { + 'terrainName': 'Mountain' + 'type': 'mountain' + 'floors': 'mountain_floor' + 'borders': 'mountain_walls' + 'borderNoise': 1 + 'borderSize': 1 + 'borderThickness': 1 + 'decorations': { + 'hero': { + 'num': [1, 1] + 'width': 2 + 'height': 2 + 'clusters': { + 'hero': [1, 1] + } + } + } + }, + 'glacier': { + 'terrainName': 'Glacier' + 'type': 'glacier' + 'floors': 'glacier_floor' + 'borders': 'glacier_walls' + 'borderNoise': 0 + 'borderSize': 4 + 'borderThickness': 1 + 'decorations': { + 'hero': { + 'num': [1, 1] + 'width': 2 + 'height': 2 + 'clusters': { + 'hero': [1, 1] + } + } + 'Room': { + 'num': [1,1] + 'width': [12, 20] + 'height': [8, 16] + 'thickness': [2,2] + 'cluster': 'glacier_walls' + } + } + } } presetSizes = { diff --git a/app/views/editor/level/modals/LevelVersionsModal.coffee b/app/views/editor/level/modals/LevelVersionsModal.coffee index 21cb5a125..9d8f5eef3 100644 --- a/app/views/editor/level/modals/LevelVersionsModal.coffee +++ b/app/views/editor/level/modals/LevelVersionsModal.coffee @@ -1,4 +1,4 @@ -VersionsModal = require 'views/modal/VersionsModal' +VersionsModal = require 'views/editor/modal/VersionsModal' module.exports = class LevelVersionsModal extends VersionsModal id: 'editor-level-versions-view' @@ -6,4 +6,4 @@ module.exports = class LevelVersionsModal extends VersionsModal page: 'level' constructor: (options, @ID) -> - super options, ID, require 'models/Level' + super options, @ID, require 'models/Level' diff --git a/app/views/editor/level/modals/NewAchievementModal.coffee b/app/views/editor/level/modals/NewAchievementModal.coffee index 589f2c573..0978b3300 100644 --- a/app/views/editor/level/modals/NewAchievementModal.coffee +++ b/app/views/editor/level/modals/NewAchievementModal.coffee @@ -1,4 +1,4 @@ -NewModelModal = require 'views/modal/NewModelModal' +NewModelModal = require 'views/editor/modal/NewModelModal' template = require 'templates/editor/level/modal/new-achievement' forms = require 'core/forms' Achievement = require 'models/Achievement' diff --git a/app/views/editor/level/modals/SaveLevelModal.coffee b/app/views/editor/level/modals/SaveLevelModal.coffee index 1aca37a80..3d890ff45 100644 --- a/app/views/editor/level/modals/SaveLevelModal.coffee +++ b/app/views/editor/level/modals/SaveLevelModal.coffee @@ -1,4 +1,4 @@ -SaveVersionModal = require 'views/modal/SaveVersionModal' +SaveVersionModal = require 'views/editor/modal/SaveVersionModal' template = require 'templates/editor/level/save' forms = require 'core/forms' LevelComponent = require 'models/LevelComponent' @@ -20,6 +20,7 @@ module.exports = class SaveLevelModal extends SaveVersionModal constructor: (options) -> super options @level = options.level + @buildTime = options.buildTime getRenderData: (context={}) -> context = super(context) @@ -60,6 +61,7 @@ module.exports = class SaveLevelModal extends SaveVersionModal commitLevel: (e) -> e.preventDefault() + @level.set 'buildTime', @buildTime modelsToSave = [] formsToSave = [] for form in @$el.find('form') @@ -97,7 +99,7 @@ module.exports = class SaveLevelModal extends SaveVersionModal tuples = _.zip(modelsToSave, formsToSave) for [newModel, form] in tuples newModel.updateI18NCoverage() if newModel.get('i18nCoverage') - res = newModel.save() + res = newModel.save(null, {type: 'POST'}) # Override PUT so we can trigger postNewVersion logic do (newModel, form) => res.error => @hideLoading() diff --git a/app/views/editor/level/modals/WorldSelectModal.coffee b/app/views/editor/level/modals/WorldSelectModal.coffee index 0ed97a2e9..35dd8d36b 100644 --- a/app/views/editor/level/modals/WorldSelectModal.coffee +++ b/app/views/editor/level/modals/WorldSelectModal.coffee @@ -47,7 +47,6 @@ module.exports = class WorldSelectModal extends ModalView canvases.attr('width', currentView.$el.width()*.8-70) canvases.attr('height', currentView.$el.height()*.6) @surface = new Surface @world, normalCanvas, webGLCanvas, { - wizards: false paths: false grid: true navigateToSelection: false diff --git a/app/views/editor/level/scripts/ScriptsTabView.coffee b/app/views/editor/level/scripts/ScriptsTabView.coffee index 98b48f7c1..51fcfa208 100644 --- a/app/views/editor/level/scripts/ScriptsTabView.coffee +++ b/app/views/editor/level/scripts/ScriptsTabView.coffee @@ -4,6 +4,7 @@ Level = require 'models/Level' Surface = require 'lib/surface/Surface' nodes = require './../treema_nodes' defaultScripts = require 'lib/DefaultScripts' +require 'vendor/treema' module.exports = class ScriptsTabView extends CocoView id: 'editor-level-scripts-tab-view' @@ -90,7 +91,7 @@ module.exports = class ScriptsTabView extends CocoView newPath = selected.getPath() return if newPath is @selectedScriptPath - #@scriptTreema?.destroy() # TODO: get this to work + #@scriptTreema?.destroy() # TODO: get this to work @scriptTreema = @$el.find('#script-treema').treema treemaOptions @scriptTreema.build() @scriptTreema.childrenTreemas?.noteChain?.open() @@ -117,12 +118,13 @@ module.exports = class ScriptsTabView extends CocoView treema.enableTracking() onScriptChanged: => + return unless @selectedScriptPath @scriptsTreema.set(@selectedScriptPath, @scriptTreema.data) onThangsEdited: (e) -> # Update in-place so existing Treema nodes refer to the same array. @thangIDs?.splice(0, @thangIDs.length, @getThangIDs()...) - + onWindowResize: (e) => @$el.find('#scripts-treema').collapse('show') if $('body').width() > 800 @@ -157,7 +159,6 @@ class ScriptNode extends TreemaObjectNode tabToCurrentScript: -> @settings.view.scriptTreema?.keepFocus() - window.v = @settings.view firstRow = @settings.view.scriptTreema?.$el.find('.treema-node:visible').data('instance') return unless firstRow? firstRow.select() diff --git a/app/views/editor/level/settings/SettingsTabView.coffee b/app/views/editor/level/settings/SettingsTabView.coffee index 0b30038b8..f3a42a20d 100644 --- a/app/views/editor/level/settings/SettingsTabView.coffee +++ b/app/views/editor/level/settings/SettingsTabView.coffee @@ -4,6 +4,7 @@ Level = require 'models/Level' Surface = require 'lib/surface/Surface' nodes = require './../treema_nodes' {me} = require 'core/auth' +require 'vendor/treema' module.exports = class SettingsTabView extends CocoView id: 'editor-level-settings-tab-view' @@ -13,7 +14,8 @@ module.exports = class SettingsTabView extends CocoView # not thangs or scripts or the backend stuff editableSettings: [ 'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals', - 'type', 'terrain', 'showsGuide', 'banner', 'employerDescription', 'loadingTip' + 'type', 'terrain', 'showsGuide', 'banner', 'employerDescription', 'loadingTip', 'requiresSubscription', + 'tasks', 'helpVideos', 'replayable', 'scoreTypes', 'concepts' ] subscriptions: diff --git a/app/views/editor/level/systems/LevelSystemEditView.coffee b/app/views/editor/level/systems/LevelSystemEditView.coffee index 76353dc3c..c76a5372e 100644 --- a/app/views/editor/level/systems/LevelSystemEditView.coffee +++ b/app/views/editor/level/systems/LevelSystemEditView.coffee @@ -3,7 +3,8 @@ template = require 'templates/editor/level/system/level-system-edit-view' LevelSystem = require 'models/LevelSystem' SystemVersionsModal = require 'views/editor/level/systems/SystemVersionsModal' PatchesView = require 'views/editor/PatchesView' -SaveVersionModal = require 'views/modal/SaveVersionModal' +SaveVersionModal = require 'views/editor/modal/SaveVersionModal' +require 'vendor/treema' module.exports = class LevelSystemEditView extends CocoView id: 'level-system-edit-view' diff --git a/app/views/editor/level/systems/NewLevelSystemModal.coffee b/app/views/editor/level/systems/NewLevelSystemModal.coffee index 13e6cac38..193a50c5d 100644 --- a/app/views/editor/level/systems/NewLevelSystemModal.coffee +++ b/app/views/editor/level/systems/NewLevelSystemModal.coffee @@ -22,7 +22,7 @@ module.exports = class NewLevelSystemModal extends ModalView system.set 'name', name system.set 'code', system.get('code').replace(/Jitter/g, name) system.set 'permissions', [{access: 'owner', target: me.id}] # Private until saved in a published Level - res = system.save() + res = system.save(null, {type: 'POST'}) # Override PUT so we can trigger postFirstVersion logic return unless res @showLoading() diff --git a/app/views/editor/level/systems/SystemVersionsModal.coffee b/app/views/editor/level/systems/SystemVersionsModal.coffee index 75d0a4ba0..027516a8b 100755 --- a/app/views/editor/level/systems/SystemVersionsModal.coffee +++ b/app/views/editor/level/systems/SystemVersionsModal.coffee @@ -1,4 +1,4 @@ -VersionsModal = require 'views/modal/VersionsModal' +VersionsModal = require 'views/editor/modal/VersionsModal' module.exports = class SystemVersionsModal extends VersionsModal id: 'editor-system-versions-view' @@ -6,4 +6,4 @@ module.exports = class SystemVersionsModal extends VersionsModal page: 'system' constructor: (options, @ID) -> - super options, ID, require 'models/LevelSystem' + super options, @ID, require 'models/LevelSystem' diff --git a/app/views/editor/level/systems/SystemsTabView.coffee b/app/views/editor/level/systems/SystemsTabView.coffee index 3969a9e12..dbebf0c8c 100644 --- a/app/views/editor/level/systems/SystemsTabView.coffee +++ b/app/views/editor/level/systems/SystemsTabView.coffee @@ -6,6 +6,7 @@ LevelSystemEditView = require './LevelSystemEditView' NewLevelSystemModal = require './NewLevelSystemModal' AddLevelSystemModal = require './AddLevelSystemModal' nodes = require '../treema_nodes' +require 'vendor/treema' module.exports = class SystemsTabView extends CocoView id: 'systems-tab-view' @@ -127,7 +128,7 @@ module.exports = class SystemsTabView extends CocoView @levelSystemEditView = null onTerrainChanged: (e) -> - defaultPathfinding = e.terrain in ['Dungeon', 'Indoor'] + defaultPathfinding = e.terrain in ['Dungeon', 'Indoor', 'Mountain', 'Glacier', 'Volcano'] changed = false if AI = @systemsTreema.get 'original=528110f30268d018e3000001' unless AI.config?.findsPaths is defaultPathfinding diff --git a/app/views/editor/level/thangs/AddThangsView.coffee b/app/views/editor/level/thangs/AddThangsView.coffee index a88b66cce..cdc50db1e 100644 --- a/app/views/editor/level/thangs/AddThangsView.coffee +++ b/app/views/editor/level/thangs/AddThangsView.coffee @@ -33,7 +33,7 @@ module.exports = class AddThangsView extends CocoView models = @supermodel.getModels(ThangType) thangTypes = _.uniq models, false, (thangType) -> thangType.get('original') - thangTypes = _.reject thangTypes, (thangType) -> thangType.get('kind') in ['Mark', 'Item'] + thangTypes = _.reject thangTypes, (thangType) -> thangType.get('kind') in ['Mark', 'Item', undefined] groupMap = {} for thangType in thangTypes kind = thangType.get('kind') @@ -62,7 +62,7 @@ module.exports = class AddThangsView extends CocoView @buildAddThangPopovers() buildAddThangPopovers: -> - @$el.find('#thangs-list .add-thang-palette-icon').tooltip(container: 'body', animation: false) + @$el.find('#thangs-list .add-thang-palette-icon').addClass('has-tooltip').tooltip(container: 'body', animation: false) runSearch: (e) => if e?.which is 27 diff --git a/app/views/editor/level/thangs/LevelThangEditView.coffee b/app/views/editor/level/thangs/LevelThangEditView.coffee index 57be3b830..14621051d 100644 --- a/app/views/editor/level/thangs/LevelThangEditView.coffee +++ b/app/views/editor/level/thangs/LevelThangEditView.coffee @@ -46,7 +46,7 @@ module.exports = class LevelThangEditView extends CocoView level: @level world: @world - if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] then options.thangType = thangType + if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] then options.thangType = thangType @thangComponentEditView = new ThangComponentsEditView options @listenTo @thangComponentEditView, 'components-changed', @onComponentsChanged @@ -57,7 +57,6 @@ module.exports = class LevelThangEditView extends CocoView thangTypeName = thangType?.get('name') or 'None' input.val(thangTypeName) @$el.find('#thang-type-link span').text(thangTypeName) - window.input = input @hideLoading() navigateToAllThangs: -> diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index 35162b674..c1f816928 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -10,6 +10,7 @@ Surface = require 'lib/surface/Surface' Thang = require 'lib/world/thang' LevelThangEditView = require './LevelThangEditView' ComponentsCollection = require 'collections/ComponentsCollection' +require 'vendor/treema' # Moving the screen while dragging thangs constants MOVE_MARGIN = 0.15 @@ -56,14 +57,23 @@ module.exports = class ThangsTabView extends CocoView shortcuts: 'esc': 'selectAddThang' 'delete, del, backspace': 'deleteSelectedExtantThang' - 'left': -> @moveAddThangSelection -1 - 'right': -> @moveAddThangSelection 1 'ctrl+z, ⌘+z': 'undo' 'ctrl+shift+z, ⌘+shift+z': 'redo' - 'alt+left': -> @rotateSelectedThangBy(Math.PI) - 'alt+right': -> @rotateSelectedThangBy(0) - 'alt+up': -> @rotateSelectedThangBy(-Math.PI/2) - 'alt+down': -> @rotateSelectedThangBy(Math.PI/2) + 'alt+c': 'toggleSelectedThangCollision' + 'left': -> @moveSelectedThangBy -1, 0 + 'right': -> @moveSelectedThangBy 1, 0 + 'up': -> @moveSelectedThangBy 0, 1 + 'down': -> @moveSelectedThangBy 0, -1 + 'alt+left': -> @rotateSelectedThangTo Math.PI unless key.shift + 'alt+right': -> @rotateSelectedThangTo 0 unless key.shift + 'alt+up': -> @rotateSelectedThangTo -Math.PI / 2 + 'alt+down': -> @rotateSelectedThangTo Math.PI / 2 + 'alt+shift+left': -> @rotateSelectedThangBy Math.PI / 16 + 'alt+shift+right': -> @rotateSelectedThangBy -Math.PI / 16 + 'shift+left': -> @resizeSelectedThangBy -1, 0 + 'shift+right': -> @resizeSelectedThangBy 1, 0 + 'shift+up': -> @resizeSelectedThangBy 0, 1 + 'shift+down': -> @resizeSelectedThangBy 0, -1 constructor: (options) -> super options @@ -82,7 +92,7 @@ module.exports = class ThangsTabView extends CocoView return context unless @supermodel.finished() thangTypes = (thangType.attributes for thangType in @supermodel.getModels(ThangType)) thangTypes = _.uniq thangTypes, false, 'original' - thangTypes = _.reject thangTypes, kind: 'Mark' + thangTypes = _.reject thangTypes, (tt) -> tt.kind in ['Mark', undefined] groupMap = {} for thangType in thangTypes groupMap[thangType.kind] ?= [] @@ -192,7 +202,6 @@ module.exports = class ThangsTabView extends CocoView webGLCanvas = $('canvas#webgl-surface', @$el) normalCanvas = $('canvas#normal-surface', @$el) @surface = new Surface @world, normalCanvas, webGLCanvas, { - wizards: false paths: false coords: true grid: true @@ -203,6 +212,7 @@ module.exports = class ThangsTabView extends CocoView } @surface.playing = false @surface.setWorld @world + @surface.lankBoss.suppressSelectionSounds = true @centerCamera() centerCamera: -> @@ -472,8 +482,21 @@ module.exports = class ThangsTabView extends CocoView return unless @selectedExtantThang thang = @getThangByID(@selectedExtantThang.id) @thangsTreema.delete(@pathForThang(thang)) + @deleteEmptyTreema(thang) Thang.resetThangIDs() # TODO: find some way to do this when we delete from treema, too + deleteEmptyTreema: (thang)-> + thangType = @supermodel.getModelByOriginal ThangType, thang.thangType + children = @thangsTreema.childrenTreemas + thangKind = children[thangType.get('kind', true)].data + thangName = thangKind[thangType.get('name', true)] + if Object.keys(thangName).length == 0 + folderPath = [thangType.get('kind', true), thangType.get('name', true)].join('/') + @thangsTreema.delete(folderPath) + if Object.keys(thangKind).length == 0 + folderPath = [thangType.get('kind', true)].join('/') + @thangsTreema.delete(folderPath) + groupThangs: (thangs) -> # array of thangs -> foldered thangs grouped = {} @@ -515,7 +538,7 @@ module.exports = class ThangsTabView extends CocoView prefix += segment if not @thangsTreema.get(prefix) then @thangsTreema.set(prefix, {}) - onThangsChanged: => + onThangsChanged: (skipSerialization) => return if @hush # keep the thangs in the same order as before, roughly @@ -526,6 +549,7 @@ module.exports = class ThangsTabView extends CocoView @level.set 'thangs', thangs return if @editThangView + return if skipSerialization serializedLevel = @level.serialize @supermodel, null, null, true try @world.loadFromLevel serializedLevel, false @@ -558,14 +582,14 @@ module.exports = class ThangsTabView extends CocoView if batchInsert if thangType.get('name') is 'Hero Placeholder' thangID = 'Hero Placeholder' - return if not (@level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop']) or @getThangByID(thangID) + return if not (@level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder']) or @getThangByID(thangID) else thangID = "Random #{thangType.get('name')} #{@thangsBatch.length}" else thangID = Thang.nextID(thangType.get('name'), @world) until thangID and not @getThangByID(thangID) if @cloneSourceThang components = _.cloneDeep @getThangByID(@cloneSourceThang.id).components - else if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] + else if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] components = [] # Load them all from default ThangType Components else components = _.cloneDeep thangType.get('components') ? [] @@ -614,9 +638,9 @@ module.exports = class ThangsTabView extends CocoView onSpriteContextMenu: (e) -> {clientX, clientY} = e.originalEvent.nativeEvent if @addThangType - $('#duplicate a').html 'Stop Duplicate' + $('#duplicate a').html $.i18n.t 'editor.stop_duplicate' else - $('#duplicate a').html 'Duplicate' + $('#duplicate a').html $.i18n.t 'editor.duplicate' $('#contextmenu').css { position: 'fixed', left: clientX, top: clientY } $('#contextmenu').show() @@ -633,18 +657,56 @@ module.exports = class ThangsTabView extends CocoView onClickRotationButton: (e) -> $('#contextmenu').hide() rotation = parseFloat($(e.target).closest('button').data('rotation')) - @rotateSelectedThangBy rotation * Math.PI + @rotateSelectedThangTo rotation * Math.PI + + modifySelectedThangComponentConfig: (thang, componentOriginal, modificationFunction) -> + return unless thang + @hush = true + thangData = @getThangByID thang.id + thangData = $.extend true, {}, thangData + unless component = _.find thangData.components, {original: componentOriginal} + component = original: componentOriginal, config: {}, majorVersion: 0 + thangData.components.push component + modificationFunction component + @thangsTreema.set @pathForThang(thangData), thangData + @hush = false + @onThangsChanged true + thang.stateChanged = true + lank = @surface.lankBoss.lanks[thang.id] + lank.update true + lank.marks.debug?.destroy() + delete lank.marks.debug + lank.setDebug true + + rotateSelectedThangTo: (radians) -> + @modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) => + component.config.rotation = radians + @selectedExtantThang.rotation = component.config.rotation rotateSelectedThangBy: (radians) -> - return unless @selectedExtantThang - @hush = true - thangData = @getThangByID(@selectedExtantThang.id) - thangData = $.extend(true, {}, thangData) - component = _.find thangData.components, {original: LevelComponent.PhysicalID} - component.config.rotation = radians - @thangsTreema.set(@pathForThang(thangData), thangData) - @hush = false - @onThangsChanged() + @modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) => + component.config.rotation = ((component.config.rotation ? 0) + radians) % (2 * Math.PI) + @selectedExtantThang.rotation = component.config.rotation + + moveSelectedThangBy: (xDir, yDir) -> + @modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) => + component.config.pos.x += 0.5 * xDir + component.config.pos.y += 0.5 * yDir + @selectedExtantThang.pos.x = component.config.pos.x + @selectedExtantThang.pos.y = component.config.pos.y + + resizeSelectedThangBy: (xDir, yDir) -> + @modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) => + component.config.width = (component.config.width ? 4) + 0.5 * xDir + component.config.height = (component.config.height ? 4) + 0.5 * yDir + @selectedExtantThang.width = component.config.width + @selectedExtantThang.height = component.config.height + + toggleSelectedThangCollision: -> + @modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.CollidesID, (component) => + component.config ?= {} + component.config.collisionCategory = if component.config.collisionCategory is 'none' then 'ground' else 'none' + @selectedExtantThang.collisionCategory = component.config.collisionCategory toggleThangsContainer: (e) -> $('#all-thangs').toggleClass('hide') diff --git a/app/views/editor/level/treema_nodes.coffee b/app/views/editor/level/treema_nodes.coffee index e04092c8b..2cc00b7c3 100644 --- a/app/views/editor/level/treema_nodes.coffee +++ b/app/views/editor/level/treema_nodes.coffee @@ -2,6 +2,7 @@ WorldSelectModal = require './modals/WorldSelectModal' ThangType = require '/models/ThangType' LevelComponent = require 'models/LevelComponent' CocoCollection = require 'collections/CocoCollection' +require 'vendor/treema' makeButton = -> $('<a class="btn btn-primary btn-xs treema-map-button"><span class="glyphicon glyphicon-screenshot"></span></a>') shorten = (f) -> parseFloat(f.toFixed(1)) diff --git a/app/views/modal/ConfirmModal.coffee b/app/views/editor/modal/ConfirmModal.coffee similarity index 83% rename from app/views/modal/ConfirmModal.coffee rename to app/views/editor/modal/ConfirmModal.coffee index 712325d2c..8017aec21 100644 --- a/app/views/modal/ConfirmModal.coffee +++ b/app/views/editor/modal/ConfirmModal.coffee @@ -1,5 +1,5 @@ -ModalView = require '../core/ModalView' -template = require 'templates/modal/confirm' +ModalView = require '../../core/ModalView' +template = require 'templates/editor/modal/confirm-modal' module.exports = class ConfirmModal extends ModalView id: 'confirm-modal' diff --git a/app/views/modal/NewModelModal.coffee b/app/views/editor/modal/NewModelModal.coffee similarity index 83% rename from app/views/modal/NewModelModal.coffee rename to app/views/editor/modal/NewModelModal.coffee index 1da2680fe..7debf7e00 100644 --- a/app/views/modal/NewModelModal.coffee +++ b/app/views/editor/modal/NewModelModal.coffee @@ -1,5 +1,5 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/modal/new_model' +template = require 'templates/editor/modal/new-model-modal' forms = require 'core/forms' module.exports = class NewModelModal extends ModalView @@ -15,13 +15,14 @@ module.exports = class NewModelModal extends ModalView super options @modelClass = options.model @modelLabel = options.modelLabel + @newModelTitle = "editor.new_#{_.string.slugify @modelLabel}_title" @properties = options.properties $('#name').ready @focusOnName getRenderData: -> c = super() c.modelLabel = @modelLabel - #c.newModelTitle = @newModelTitle + c.newModelTitle = @newModelTitle c makeNewModel: -> @@ -36,7 +37,7 @@ module.exports = class NewModelModal extends ModalView onModelSubmitted: (e) -> e.preventDefault() model = @makeNewModel() - res = model.save() + res = model.save(null, {type: 'POST'}) # Override PUT so we can trigger postFirstVersion logic if needed return unless res forms.clearFormAlerts @$el diff --git a/app/views/modal/SaveVersionModal.coffee b/app/views/editor/modal/SaveVersionModal.coffee similarity index 97% rename from app/views/modal/SaveVersionModal.coffee rename to app/views/editor/modal/SaveVersionModal.coffee index f99061908..91cd3e997 100644 --- a/app/views/modal/SaveVersionModal.coffee +++ b/app/views/editor/modal/SaveVersionModal.coffee @@ -1,5 +1,5 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/modal/save_version' +template = require 'templates/editor/modal/save-version-modal' DeltaView = require 'views/editor/DeltaView' Patch = require 'models/Patch' forms = require 'core/forms' diff --git a/app/views/modal/VersionsModal.coffee b/app/views/editor/modal/VersionsModal.coffee similarity index 93% rename from app/views/modal/VersionsModal.coffee rename to app/views/editor/modal/VersionsModal.coffee index 4bf9b85a6..8be2ca97c 100755 --- a/app/views/modal/VersionsModal.coffee +++ b/app/views/editor/modal/VersionsModal.coffee @@ -1,5 +1,5 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/modal/versions' +template = require 'templates/editor/modal/versions-modal' DeltaView = require 'views/editor/DeltaView' PatchModal = require 'views/editor/PatchModal' nameLoader = require 'core/NameLoader' @@ -12,8 +12,7 @@ class VersionsViewCollection extends CocoCollection initialize: (@url, @levelID, @model) -> super() - @url = url + levelID + '/versions' - @model = model + @url = @url + @levelID + '/versions' module.exports = class VersionsModal extends ModalView template: template @@ -30,7 +29,7 @@ module.exports = class VersionsModal extends ModalView constructor: (options, @ID, @model) -> super options - @original = new model(_id: @ID) + @original = new @model(_id: @ID) @original = @supermodel.loadModel(@original, 'document').model @listenToOnce(@original, 'sync', @onViewSync) diff --git a/app/views/editor/poll/PollEditView.coffee b/app/views/editor/poll/PollEditView.coffee new file mode 100644 index 000000000..2d4f27300 --- /dev/null +++ b/app/views/editor/poll/PollEditView.coffee @@ -0,0 +1,135 @@ +RootView = require 'views/core/RootView' +template = require 'templates/editor/poll/poll-edit-view' +Poll = require 'models/Poll' +UserPollsRecord = require 'models/UserPollsRecord' +PollModal = require 'views/play/modal/PollModal' +ConfirmModal = require 'views/editor/modal/ConfirmModal' +PatchesView = require 'views/editor/PatchesView' +errors = require 'core/errors' +app = require 'core/application' + +module.exports = class PollEditView extends RootView + id: 'editor-poll-edit-view' + template: template + + events: + 'click #save-button': 'savePoll' + 'click #delete-button': 'confirmDeletion' + + constructor: (options, @pollID) -> + super options + @loadPoll() + @loadUserPollsRecord() + @pushChangesToPreview = _.throttle(@pushChangesToPreview, 500) + + loadPoll: -> + @poll = new Poll _id: @pollID + @poll.saveBackups = true + @supermodel.loadModel @poll, 'poll' + + loadUserPollsRecord: -> + url = "/db/user.polls.record/-/user/#{me.id}" + @userPollsRecord = new UserPollsRecord().setURL url + onRecordSync = -> + return if @destroyed + @userPollsRecord.url = -> '/db/user.polls.record/' + @id + @listenToOnce @userPollsRecord, 'sync', onRecordSync + @userPollsRecord = @supermodel.loadModel(@userPollsRecord, 'user_polls_record').model + onRecordSync.call @ if @userPollsRecord.loaded + + onLoaded: -> + super() + @buildTreema() + @listenTo @poll, 'change', => + @poll.updateI18NCoverage() + @treema.set('/', @poll.attributes) + + buildTreema: -> + return if @treema? or (not @poll.loaded) + data = $.extend(true, {}, @poll.attributes) + options = + data: data + filePath: "db/poll/#{@poll.get('_id')}" + schema: Poll.schema + readOnly: me.get('anonymous') + callbacks: + change: => @pushChangesToPreview() unless @hush + @treema = @$el.find('#poll-treema').treema(options) + @treema.build() + @treema.childrenTreemas.answers?.open 1 + @pushChangesToPreview() + + getRenderData: (context={}) -> + context = super(context) + context.poll = @poll + context.authorized = me.isAdmin() + context + + afterRender: -> + super() + return unless @supermodel.finished() + @pushChangesToPreview() + @patchesView = @insertSubView(new PatchesView(@poll), @$el.find('.patches-view')) + @patchesView.load() + + pushChangesToPreview: => + return unless @treema + @$el.find('#poll-view').empty() + for key, value of @treema.data + @poll.set key, value + @pollModal?.destroy() + @pollModal = new PollModal supermodel: @supermodel, poll: @poll, userPollsRecord: @userPollsRecord + @pollModal.render() + $('#poll-view').empty().append @pollModal.el + #pollModal.afterInsert() # This blurs the active input; don't do it + @pollModal.$el.removeClass('modal fade').show() + @pollModal.on 'vote-updated', => + @hush = true + @treema.set '/answers', @pollModal.poll.get('answers') + @hush = false + + savePoll: (e) -> + @treema.endExistingEdits() + for key, value of @treema.data + @poll.set(key, value) + + res = @poll.save() + + res.error (collection, response, options) => + console.error response + + res.success => + url = "/editor/poll/#{@poll.get('slug') or @poll.id}" + document.location.href = url + + confirmDeletion: -> + renderData = + 'confirmTitle': 'Are you really sure?' + 'confirmBody': 'This will completely delete the poll, potentially breaking a lot of stuff you don\'t want breaking. Are you entirely sure?' + 'confirmDecline': 'Not really' + 'confirmConfirm': 'Definitely' + + confirmModal = new ConfirmModal renderData + confirmModal.on 'confirm', @deletePoll + @openModalView confirmModal + + deletePoll: => + console.debug 'deleting' + $.ajax + type: 'DELETE' + success: -> + noty + timeout: 5000 + text: 'Aaaand it\'s gone.' + type: 'success' + layout: 'topCenter' + _.delay -> + app.router.navigate '/editor/poll', trigger: true + , 500 + error: (jqXHR, status, error) -> + console.error jqXHR + timeout: 5000 + text: "Deleting poll failed with error code #{jqXHR.status}" + type: 'error' + layout: 'topCenter' + url: "/db/poll/#{@poll.id}" diff --git a/app/views/editor/poll/PollSearchView.coffee b/app/views/editor/poll/PollSearchView.coffee new file mode 100644 index 000000000..8eec2c9c8 --- /dev/null +++ b/app/views/editor/poll/PollSearchView.coffee @@ -0,0 +1,19 @@ +SearchView = require 'views/common/SearchView' + +module.exports = class PollSearchView extends SearchView + id: 'editor-poll-home-view' + modelLabel: 'Poll' + model: require 'models/Poll' + modelURL: '/db/poll' + tableTemplate: require 'templates/editor/poll/poll-search-table' + projection: ['name', 'description', 'slug', 'priority', 'created'] + + getRenderData: -> + context = super() + context.currentEditor = 'editor.poll_title' + context.currentNew = 'editor.new_poll_title' + context.currentNewSignup = 'editor.new_poll_title_login' + context.currentSearch = 'editor.poll_search_title' + context.newModelsAdminOnly = true + context.unauthorized = true unless me.isAdmin() + context diff --git a/app/views/editor/thang/ThangTypeColorsTabView.coffee b/app/views/editor/thang/ThangTypeColorsTabView.coffee index 40c6b2ca2..d2a19f4bb 100644 --- a/app/views/editor/thang/ThangTypeColorsTabView.coffee +++ b/app/views/editor/thang/ThangTypeColorsTabView.coffee @@ -2,6 +2,7 @@ CocoView = require 'views/core/CocoView' template = require 'templates/editor/thang/colors_tab' SpriteBuilder = require 'lib/sprites/SpriteBuilder' {hexToHSL} = require 'core/utils' +require 'vendor/treema' module.exports = class ThangTypeColorsTabView extends CocoView id: 'editor-thang-colors-tab-view' @@ -162,7 +163,6 @@ module.exports = class ThangTypeColorsTabView extends CocoView colors = {} @buttons.find('button').each (i, button) -> return unless $(button).hasClass('selected') - window.button = button colors[$(button).val()] = true shapes = [] diff --git a/app/views/editor/thang/ThangTypeEditView.coffee b/app/views/editor/thang/ThangTypeEditView.coffee index f980becb2..b79d3ff9a 100644 --- a/app/views/editor/thang/ThangTypeEditView.coffee +++ b/app/views/editor/thang/ThangTypeEditView.coffee @@ -5,6 +5,7 @@ Lank = require 'lib/surface/Lank' LayerAdapter = require 'lib/surface/LayerAdapter' Camera = require 'lib/surface/Camera' DocumentFiles = require 'collections/DocumentFiles' +require 'vendor/treema' # in the template, but need to require to load them require 'views/modal/RevertModal' @@ -16,11 +17,113 @@ ThangTypeColorsTabView = require './ThangTypeColorsTabView' PatchesView = require 'views/editor/PatchesView' ForkModal = require 'views/editor/ForkModal' VectorIconSetupModal = require 'views/editor/thang/VectorIconSetupModal' -SaveVersionModal = require 'views/modal/SaveVersionModal' +SaveVersionModal = require 'views/editor/modal/SaveVersionModal' template = require 'templates/editor/thang/thang-type-edit-view' storage = require 'core/storage' -CENTER = {x: 200, y: 300} +CENTER = {x: 200, y: 400} + +commonTasks = [ + 'Upload the art.' + 'Set up the vector icon.' +] + +displayedThangTypeTasks = [ + 'Configure the idle action.' + 'Configure the positions (registration point, etc.).' + 'Set shadow diameter to 0 if needed.' + 'Set scale to 0.3, 0.5, or whatever is appropriate.' + 'Set rotation to isometric if needed.' + 'Set accurate Physical size, shape, and default z.' + 'Set accurate Collides collision information if needed.' + 'Double-check that fixedRotation is accurate, if it collides.' +] + +animatedThangTypeTasks = displayedThangTypeTasks.concat [ + 'Configure the non-idle actions.' + 'Configure any per-action registration points needed.' + 'Add flipX per action if needed to face to the right.' + 'Make sure any death and attack actions do not loop.' + 'Add defaultSimlish if needed.' + 'Add selection sounds if needed.' + 'Add per-action sound triggers.' + 'Add team color groups.' +] + +containerTasks = displayedThangTypeTasks.concat [ + 'Select viable terrains if not universal.' + 'Set Exists stateless: true if needed.' +] + +purchasableTasks = [ + 'Add a tier, or 10 + desired tier if not ready yet.' + 'Add a gem cost.' + 'Write a description.' + 'Click the Populate i18n button.' +] + +defaultTasks = + Unit: commonTasks.concat animatedThangTypeTasks.concat [ + 'Start a new name category in names.coffee if needed.' + 'Set to Allied to correct team (ogres, humans, or neutral).' + 'Add AutoTargetsNearest or FightsBack if needed.' + 'Add other Components like Shoots or Casts if needed.' + 'Configure other Components, like Moves, Attackable, Attacks, etc.' + 'Override the HasAPI type if it will not be correctly inferred.' + 'Add to Existence System power table.' + ] + Hero: commonTasks.concat animatedThangTypeTasks.concat purchasableTasks.concat [ + 'Set the hero class.' + 'Add Extended Hero Name.' + 'Upload Hero Doll Images.' + 'Start a new name category in names.coffee.' + 'Set up hero stats in Equips, Attackable, Moves.' + 'Set Collects collectRange to 2, Sees visualRange to 60.' + 'Add any custom hero abilities.' + 'Add to ThangType model hard-coded hero ids/classes list.' + 'Add to LevelHUDView hard-coded hero short names list.' + 'Add to InventoryView hard-coded hero gender list.' + 'Add to PlayHeroesModal hard-coded hero positioning logic.' + 'Add as unlock to a level and add unlockLevelName here.' + ] + Floor: commonTasks.concat containerTasks.concat [ + 'Add 10 x 8.5 snapping.' + 'Set fixed rotation.' + 'Make sure everything is scaled to tile perfectly.' + 'Adjust SingularSprite floor scale list if necessary.' + ] + Wall: commonTasks.concat containerTasks.concat [ + 'Add 4x4 snapping.' + 'Set fixed rotation.' + 'Set up and tune complicated wall-face actions.' + 'Make sure everything is scaled to tile perfectly.' + ] + Doodad: commonTasks.concat containerTasks.concat [ + 'Add to GenerateTerrainModal logic if needed.' + ] + Misc: commonTasks.concat [ + 'Add any misc tasks for this misc ThangType.' + ] + Mark: commonTasks.concat [ + 'Check the animation framerate.' + 'Double-check that bottom of mark is just touching registration point.' + ] + Item: commonTasks.concat purchasableTasks.concat [ + 'Set the hero class if class-specific.' + 'Upload Paper Doll Images.' + 'Configure item stats and abilities.' + ] + Missile: commonTasks.concat animatedThangTypeTasks.concat [ + 'Make sure there is a launch sound trigger.' + 'Make sure there is a hit sound trigger.' + 'Make sure there is a die animation.' + 'Add Arrow, Shell, Beam, or other missile Component.' + 'Choose Missile.leadsShots and Missile.shootsAtGround.' + 'Choose Moves.maxSpeed and other config.' + 'Choose Expires.lifespan config if needed.' + 'Set spriteType: singular if needed for proper rendering.' + 'Add HasAPI if the missile should show up in findEnemyMissiles.' + ] module.exports = class ThangTypeEditView extends RootView id: 'thang-type-edit-view' @@ -44,14 +147,16 @@ module.exports = class ThangTypeEditView extends RootView 'click #stop-button': 'stopAnimation' 'click #play-button': 'playAnimation' 'click #history-button': 'showVersionHistory' - 'click #fork-start-button': 'startForking' + 'click li:not(.disabled) > #fork-start-button': 'startForking' 'click #save-button': 'openSaveModal' 'click #patches-tab': -> @patchesView.load() 'click .play-with-level-button': 'onPlayLevel' 'click .play-with-level-parent': 'onPlayLevelSelect' 'keyup .play-with-level-input': 'onPlayLevelKeyUp' - 'click #pop-level-i18n-button': 'onPopulateLevelI18N' - + 'click li:not(.disabled) > #pop-level-i18n-button': 'onPopulateLevelI18N' + 'mousedown #canvas': 'onCanvasMouseDown' + 'mouseup #canvas': 'onCanvasMouseUp' + 'mousemove #canvas': 'onCanvasMouseMove' onClickSetVectorIcon: -> modal = new VectorIconSetupModal({}, @thangType) @@ -88,7 +193,9 @@ module.exports = class ThangTypeEditView extends RootView context.fileSizeString = @fileSizeString context - getAnimationNames: -> _.keys(@thangType.get('actions') or {}) + getAnimationNames: -> + _.sortBy _.keys(@thangType.get('actions') or {}), (a) -> + {move: 1, cast: 2, attack: 3, idle: 4, portrait: 6}[a] or 5 afterRender: -> super() @@ -286,7 +393,7 @@ module.exports = class ThangTypeEditView extends RootView movieClip.scaleX = movieClip.scaleY = scale @showSprite(movieClip) - getLankOptions: -> {resolutionFactor: @resolution, thang: @mockThang} + getLankOptions: -> {resolutionFactor: @resolution, thang: @mockThang, preloadSounds: false} showAction: (actionName) -> options = @getLankOptions() @@ -308,6 +415,7 @@ module.exports = class ThangTypeEditView extends RootView @layerAdapter.resetSpriteSheet() @layerAdapter.addLank(lank) @currentLank = lank + @currentLankOffset = null showSprite: (sprite) -> @clearDisplayObject() @@ -366,7 +474,7 @@ module.exports = class ThangTypeEditView extends RootView newThangType.set('commitMessage', e.commitMessage) newThangType.updateI18NCoverage() if newThangType.get('i18nCoverage') - res = newThangType.save() + res = newThangType.save(null, {type: 'POST'}) # Override PUT so we can trigger postNewVersion logic return unless res modal = $('#save-version-modal') @enableModalInProgress(modal) @@ -433,7 +541,9 @@ module.exports = class ThangTypeEditView extends RootView @lastKind = kind Backbone.Mediator.publish 'editor:thang-type-kind-changed', kind: kind if kind in ['Doodad', 'Floor', 'Wall'] and not @treema.data.terrains - @treema.set '/terrains', ['Grass', 'Dungeon', 'Indoor'] # So editors know to set them. + @treema.set '/terrains', ['Grass', 'Dungeon', 'Indoor', 'Desert', 'Mountain', 'Glacier', 'Volcano'] # So editors know to set them. + if not @treema.data.tasks + @treema.set '/tasks', (name: t for t in defaultTasks[kind]) onSelectNode: (e, selected) => selected = selected[0] @@ -441,7 +551,7 @@ module.exports = class ThangTypeEditView extends RootView return @stopShowingSelectedNode() if not selected path = selected.getPath() parts = path.split('/') - return @stopShowingSelectedNode() unless parts.length >= 4 and path.startsWith '/raw/' + return @stopShowingSelectedNode() unless parts.length >= 4 and _.string.startsWith path, '/raw/' key = parts[3] type = parts[2] vectorParser = new SpriteBuilder(@thangType) @@ -516,6 +626,31 @@ module.exports = class ThangTypeEditView extends RootView @childWindow = window.open("/play/level/#{scratchLevelID}", 'child_window', 'width=1024,height=560,left=10,top=10,location=0,menubar=0,scrollbars=0,status=0,titlebar=0,toolbar=0', true) @childWindow.focus() + # Canvas mouse drag handlers + + onCanvasMouseMove: (e) -> + return unless p1 = @canvasDragStart + p2 = x: e.offsetX, y: e.offsetY + offset = x: p2.x - p1.x, y: p2.y - p1.y + @currentLank.sprite.x = @currentLankOffset.x + offset.x / @scale + @currentLank.sprite.y = @currentLankOffset.y + offset.y / @scale + @canvasDragOffset = offset + + onCanvasMouseDown: (e) -> + return unless @currentLank + @canvasDragStart = x: e.offsetX, y: e.offsetY + @currentLankOffset ?= x: @currentLank.sprite.x, y: @currentLank.sprite.y + + onCanvasMouseUp: (e) -> + @canvasDragStart = null + return unless @canvasDragOffset + return unless node = @treema.getLastSelectedTreema() + offset = node.get '/' + offset.x += Math.round @canvasDragOffset.x + offset.y += Math.round @canvasDragOffset.y + @canvasDragOffset = null + node.set '/', offset + destroy: -> @camera?.destroy() super() diff --git a/app/views/editor/thang/ThangTypeSearchView.coffee b/app/views/editor/thang/ThangTypeSearchView.coffee index 7c5ff18f5..0e23c2432 100644 --- a/app/views/editor/thang/ThangTypeSearchView.coffee +++ b/app/views/editor/thang/ThangTypeSearchView.coffee @@ -6,7 +6,7 @@ module.exports = class ThangTypeSearchView extends SearchView model: require 'models/ThangType' modelURL: '/db/thang.type' tableTemplate: require 'templates/editor/thang/table' - projection: ['original', 'name', 'version', 'description', 'slug', 'kind', 'rasterIcon'] + projection: ['original', 'name', 'version', 'description', 'slug', 'kind', 'rasterIcon', 'tasks'] page: 'thang' getRenderData: -> @@ -15,6 +15,7 @@ module.exports = class ThangTypeSearchView extends SearchView context.currentNew = 'editor.new_thang_title' context.currentNewSignup = 'editor.new_thang_title_login' context.currentSearch = 'editor.thang_search_title' + context.newModelsAdminOnly = true @$el.i18n() context diff --git a/app/views/editor/thang/ThangTypeVersionsModal.coffee b/app/views/editor/thang/ThangTypeVersionsModal.coffee index 46bda96fc..db1049076 100755 --- a/app/views/editor/thang/ThangTypeVersionsModal.coffee +++ b/app/views/editor/thang/ThangTypeVersionsModal.coffee @@ -1,4 +1,4 @@ -VersionsModal = require 'views/modal/VersionsModal' +VersionsModal = require 'views/editor/modal/VersionsModal' module.exports = class ThangTypeVersionsModal extends VersionsModal id: 'editor-thang-versions-view' @@ -6,4 +6,4 @@ module.exports = class ThangTypeVersionsModal extends VersionsModal page: 'thang' constructor: (options, @ID) -> - super options, ID, require 'models/ThangType' + super options, @ID, require 'models/ThangType' diff --git a/app/views/editor/thang/VectorIconSetupModal.coffee b/app/views/editor/thang/VectorIconSetupModal.coffee index b2d4df3f5..d5000b924 100644 --- a/app/views/editor/thang/VectorIconSetupModal.coffee +++ b/app/views/editor/thang/VectorIconSetupModal.coffee @@ -76,8 +76,8 @@ module.exports = class VectorIconSetupModal extends ModalView updateSpriteProperties: -> @sprite.scaleX = @sprite.scaleY = @scale * @demoSize / 100 - @sprite.regX = @regX - @sprite.regY = @regY + @sprite.regX = @regX / @scale + @sprite.regY = @regY / @scale console.log 'set to', @scale, @regX, @regY onClickCenter: -> @@ -91,6 +91,8 @@ module.exports = class VectorIconSetupModal extends ModalView @regY += (b[3] - b[2]) / 2 else @regX += (b[2] - b[3]) / 2 + @regX *= @scale + @regY *= @scale @updateSpriteProperties() @stage.update() diff --git a/app/views/game-menu/GuideView.coffee b/app/views/game-menu/GuideView.coffee deleted file mode 100644 index cca8585cb..000000000 --- a/app/views/game-menu/GuideView.coffee +++ /dev/null @@ -1,58 +0,0 @@ -CocoView = require 'views/core/CocoView' -template = require 'templates/game-menu/guide-view' -Article = require 'models/Article' -utils = require 'core/utils' - -# let's implement this once we have the docs database schema set up - -module.exports = class LevelGuideView extends CocoView - template: template - id: 'guide-view' - className: 'tab-pane' - - constructor: (options) -> - @firstOnly = options.firstOnly - @docs = options?.docs ? options.level.get('documentation') ? {} - general = @docs.generalArticles or [] - specific = @docs.specificArticles or [] - - articles = options.supermodel.getModels(Article) - articleMap = {} - articleMap[article.get('original')] = article for article in articles - general = (articleMap[ref.original] for ref in general) - general = (article.attributes for article in general when article) - - @docs = specific.concat(general) - @docs = $.extend(true, [], @docs) - @docs = [@docs[0]] if @firstOnly and @docs[0] - doc.html = marked(utils.i18n doc, 'body') for doc in @docs - doc.name = (utils.i18n doc, 'name') for doc in @docs - doc.slug = _.string.slugify(doc.name) for doc in @docs - super() - - getRenderData: -> - c = super() - c.docs = @docs - c - - afterRender: -> - super() - if @docs.length is 1 - @$el.html(@docs[0].html) - else - # incredible hackiness. Getting bootstrap tabs to work shouldn't be this complex - @$el.find('.nav-tabs li:first').addClass('active') - @$el.find('.tab-content .tab-pane:first').addClass('active') - @$el.find('.nav-tabs a').click(@clickTab) - @playSound 'guide-open' - - clickTab: (e) => - @$el.find('li.active').removeClass('active') - @playSound 'guide-tab-switch' - - afterInsert: -> - super() - Backbone.Mediator.publish 'level:docs-shown', {} - - onHidden: -> - Backbone.Mediator.publish 'level:docs-hidden', {} diff --git a/app/views/i18n/I18NEditAchievementView.coffee b/app/views/i18n/I18NEditAchievementView.coffee index bddfbc3f2..be7386c3d 100644 --- a/app/views/i18n/I18NEditAchievementView.coffee +++ b/app/views/i18n/I18NEditAchievementView.coffee @@ -2,7 +2,7 @@ I18NEditModelView = require './I18NEditModelView' Achievement = require 'models/Achievement' module.exports = class I18NEditAchievementView extends I18NEditModelView - id: "i18n-edit-component-view" + id: "i18n-edit-achievement-view" modelClass: Achievement buildTranslationList: -> diff --git a/app/views/i18n/I18NEditCampaignView.coffee b/app/views/i18n/I18NEditCampaignView.coffee new file mode 100644 index 000000000..40d7eeab6 --- /dev/null +++ b/app/views/i18n/I18NEditCampaignView.coffee @@ -0,0 +1,18 @@ +I18NEditModelView = require './I18NEditModelView' +Campaign = require 'models/Campaign' + +module.exports = class I18NEditCampaignView extends I18NEditModelView + id: "i18n-edit-campaign-view" + modelClass: Campaign + + buildTranslationList: -> + lang = @selectedLanguage + + # name, description + if i18n = @model.get('i18n') + if name = @model.get('name') + @wrapRow 'Campaign short name', ['name'], name, i18n[lang]?.name, [] + if fullName = @model.get('fullName') + @wrapRow 'Campaign full name', ['fullName'], fullName, i18n[lang]?.fullName, [] + if description = @model.get('description') + @wrapRow 'Campaign description', ['description'], description, i18n[lang]?.description, [] diff --git a/app/views/i18n/I18NEditComponentView.coffee b/app/views/i18n/I18NEditComponentView.coffee index 42fb3ac35..416d16c6d 100644 --- a/app/views/i18n/I18NEditComponentView.coffee +++ b/app/views/i18n/I18NEditComponentView.coffee @@ -2,7 +2,7 @@ I18NEditModelView = require './I18NEditModelView' LevelComponent = require 'models/LevelComponent' module.exports = class I18NEditComponentView extends I18NEditModelView - id: "i18n-edit-component-view" + id: 'i18n-edit-component-view' modelClass: LevelComponent buildTranslationList: -> @@ -14,7 +14,7 @@ module.exports = class I18NEditComponentView extends I18NEditModelView #- Component property descriptions if i18n = propDoc.i18n - path = ["propertyDocumentation", propDocIndex] + path = ['propertyDocumentation', propDocIndex] if _.isObject propDoc.description for progLang, description of propDoc.description @wrapRow "#{propDoc.name} description (#{progLang})", ['description', progLang], description, i18n[lang]?[progLang]?.description, path, 'markdown' @@ -22,11 +22,11 @@ module.exports = class I18NEditComponentView extends I18NEditModelView @wrapRow "#{propDoc.name} description", ['description'], propDoc.description, i18n[lang]?.description, path, 'markdown' if context = propDoc.context for key, value of context - @wrapRow "#{propDoc.name} context value", ["context", key], value, i18n[lang]?.context[key], path + @wrapRow "#{propDoc.name} context value", ['context', key], value, i18n[lang]?.context?[key], path #- Component return value descriptions if i18n = propDoc.returns?.i18n - path = ["propertyDocumentation", propDocIndex, "returns"] + path = ['propertyDocumentation', propDocIndex, 'returns'] d = propDoc.returns.description if _.isObject d for progLang, description of d.description @@ -38,7 +38,7 @@ module.exports = class I18NEditComponentView extends I18NEditModelView if propDoc.args for argDoc, argIndex in propDoc.args if i18n = argDoc.i18n - path = ["propertyDocumentation", propDocIndex, 'args', argIndex] + path = ['propertyDocumentation', propDocIndex, 'args', argIndex] if _.isObject argDoc.description for progLang, description of argDoc.description @wrapRow "#{propDoc.name} arg description #{argDoc.name} (#{progLang})", ['description', progLang], description, i18n[lang]?[progLang]?.description, path, 'markdown' diff --git a/app/views/i18n/I18NEditLevelView.coffee b/app/views/i18n/I18NEditLevelView.coffee index 307c7413b..bb797ca73 100644 --- a/app/views/i18n/I18NEditLevelView.coffee +++ b/app/views/i18n/I18NEditLevelView.coffee @@ -3,7 +3,7 @@ Level = require 'models/Level' LevelComponent = require 'models/LevelComponent' module.exports = class I18NEditLevelView extends I18NEditModelView - id: "i18n-edit-level-view" + id: 'i18n-edit-level-view' modelClass: Level buildTranslationList: -> @@ -12,22 +12,22 @@ module.exports = class I18NEditLevelView extends I18NEditModelView # name, description if i18n = @model.get('i18n') if name = @model.get('name') - @wrapRow "Level name", ['name'], name, i18n[lang]?.name, [] + @wrapRow 'Level name', ['name'], name, i18n[lang]?.name, [] if description = @model.get('description') - @wrapRow "Level description", ['description'], description, i18n[lang]?.description, [] + @wrapRow 'Level description', ['description'], description, i18n[lang]?.description, [] if loadingTip = @model.get('loadingTip') - @wrapRow "Loading tip", ['loadingTip'], loadingTip, i18n[lang]?.loadingTip, [] + @wrapRow 'Loading tip', ['loadingTip'], loadingTip, i18n[lang]?.loadingTip, [] # goals for goal, index in @model.get('goals') ? [] if i18n = goal.i18n - @wrapRow "Goal name", ['name'], goal.name, i18n[lang]?.name, ['goals', index] + @wrapRow 'Goal name', ['name'], goal.name, i18n[lang]?.name, ['goals', index] # documentation for doc, index in @model.get('documentation')?.specificArticles ? [] if i18n = doc.i18n - @wrapRow "Guide article name", ['name'], doc.name, i18n[lang]?.name, ['documentation', 'specificArticles', index] - @wrapRow "'#{doc.name}' description", ['description'], doc.description, i18n[lang]?.description, ['documentation', 'specificArticles', index], 'markdown' + @wrapRow 'Guide article name', ['name'], doc.name, i18n[lang]?.name, ['documentation', 'specificArticles', index] + @wrapRow "'#{doc.name}' body", ['body'], doc.body, i18n[lang]?.body, ['documentation', 'specificArticles', index], 'markdown' # sprite dialogues for script, scriptIndex in @model.get('scripts') ? [] @@ -37,17 +37,17 @@ module.exports = class I18NEditLevelView extends I18NEditModelView if i18n = spriteCommand.say?.i18n if spriteCommand.say.text - @wrapRow "Sprite text", ['text'], spriteCommand.say.text, i18n[lang]?.text, pathPrefix, 'markdown' + @wrapRow 'Sprite text', ['text'], spriteCommand.say.text, i18n[lang]?.text, pathPrefix, 'markdown' if spriteCommand.say.blurb - @wrapRow "Sprite blurb", ['blurb'], spriteCommand.say.blurb, i18n[lang]?.blurb, pathPrefix + @wrapRow 'Sprite blurb', ['blurb'], spriteCommand.say.blurb, i18n[lang]?.blurb, pathPrefix for response, responseIndex in spriteCommand.say?.responses ? [] if i18n = response.i18n - @wrapRow "Response button", ['text'], response.text, i18n[lang]?.text, pathPrefix.concat(['responses', responseIndex]) + @wrapRow 'Response button', ['text'], response.text, i18n[lang]?.text, pathPrefix.concat(['responses', responseIndex]) # victory modal if i18n = @model.get('victory')?.i18n - @wrapRow "Victory text", ['body'], @model.get('victory').body, i18n[lang]?.body, ['victory'], 'markdown' + @wrapRow 'Victory text', ['body'], @model.get('victory').body, i18n[lang]?.body, ['victory'], 'markdown' # code comments for thang, thangIndex in @model.get('thangs') ? [] @@ -57,4 +57,4 @@ module.exports = class I18NEditLevelView extends I18NEditModelView if (i18n = method.i18n) and (context = method.context) for key, value of context path = ['thangs', thangIndex, 'components', componentIndex, 'config', 'programmableMethods', methodName] - @wrapRow "Code comment", ["context", key], value, i18n[lang]?.context[key], path + @wrapRow 'Code comment', ['context', key], value, i18n[lang]?.context?[key], path diff --git a/app/views/i18n/I18NEditModelView.coffee b/app/views/i18n/I18NEditModelView.coffee index 826bef69b..9d4c5ac97 100644 --- a/app/views/i18n/I18NEditModelView.coffee +++ b/app/views/i18n/I18NEditModelView.coffee @@ -5,12 +5,12 @@ template = require 'templates/i18n/i18n-edit-model-view' deltasLib = require 'core/deltas' # in the template, but need to require to load them -require 'modal/RevertModal' +require 'views/modal/RevertModal' module.exports = class I18NEditModelView extends RootView className: 'editor i18n-edit-model-view' template: template - + events: 'change .translation-input': 'onInputChanged' 'change #language-select': 'onLanguageSelectChanged' @@ -22,11 +22,11 @@ module.exports = class I18NEditModelView extends RootView @model = @supermodel.loadModel(@model, 'model').model @model.saveBackups = true @selectedLanguage = me.get('preferredLanguage', true) - + showLoading: ($el) -> $el ?= @$el.find('.outer-content') super($el) - + onLoaded: -> super() @model.markToRevert() unless @model.hasLocalChanges() @@ -36,14 +36,14 @@ module.exports = class I18NEditModelView extends RootView c.model = @model c.selectedLanguage = @selectedLanguage - + @translationList = [] if @supermodel.finished() then @buildTranslationList() else [] result.index = index for result, index in @translationList c.translationList = @translationList - + c - + afterRender: -> super() @@ -55,15 +55,20 @@ module.exports = class I18NEditModelView extends RootView editors = [] @$el.find('tr[data-format="markdown"]').each((index, el) => - englishEditor = ace.edit(enEl=$(el).find('.english-value-row div')[0]) - englishEditor.el = enEl - englishEditor.setReadOnly(true) - toEditor = ace.edit(toEl=$(el).find('.to-value-row div')[0]) - toEditor.el = toEl - toEditor.on 'change', @onEditorChange - editors = editors.concat([englishEditor, toEditor]) + foundEnEl = enEl=$(el).find('.english-value-row div')[0] + if foundEnEl? + englishEditor = ace.edit(foundEnEl) + englishEditor.el = enEl + englishEditor.setReadOnly(true) + editors.push englishEditor + foundToEl = toEl=$(el).find('.to-value-row div')[0] + if foundToEl? + toEditor = ace.edit(foundToEl) + toEditor.el = toEl + toEditor.on 'change', @onEditorChange + editors.push toEditor ) - + for editor in editors session = editor.getSession() session.setTabSize 2 @@ -80,17 +85,17 @@ module.exports = class I18NEditModelView extends RootView @onTranslationChanged(rowInfo, value) wrapRow: (title, key, enValue, toValue, path, format) -> - @translationList.push { + @translationList.push { title: title, - key: key, - enValue: enValue, - toValue: toValue or '', + key: key, + enValue: enValue, + toValue: toValue or '', path: path format: format } buildTranslationList: -> [] # overwrite - + onInputChanged: (e) -> index = $(e.target).data('index') rowInfo = @translationList[index] @@ -98,10 +103,10 @@ module.exports = class I18NEditModelView extends RootView @onTranslationChanged(rowInfo, value) onTranslationChanged: (rowInfo, value) -> - + #- Navigate down to where the translation will live base = @model.attributes - + for seg in rowInfo.path base = base[seg] @@ -109,19 +114,19 @@ module.exports = class I18NEditModelView extends RootView base[@selectedLanguage] ?= {} base = base[@selectedLanguage] - + if rowInfo.key.length > 1 for seg in rowInfo.key[..-2] base[seg] ?= {} base = base[seg] - + #- Set the data in a non-kosher way - + base[rowInfo.key[rowInfo.key.length-1]] = value @model.saveBackup() - + #- Enable patch submit button - + @$el.find('#patch-submit').attr('disabled', null) onLanguageSelectChanged: (e) -> @@ -131,19 +136,24 @@ module.exports = class I18NEditModelView extends RootView me.set('preferredLanguage', @selectedLanguage) me.patch() @render() - + onSubmitPatch: (e) -> - + delta = @model.getDelta() flattened = deltasLib.flattenDelta(delta) save = _.all(flattened, (delta) -> return _.isArray(delta.o) and delta.o.length is 1 and 'i18n' in delta.dataPath ) - + + commitMessage = "Diplomat submission for lang #{@selectedLanguage}: #{flattened.length} change(s)." + save = false if @savedBefore + if save modelToSave = @model.cloneNewMinorVersion() modelToSave.updateI18NCoverage() if modelToSave.get('i18nCoverage') - + if @modelClass.schema.properties.commitMessage + modelToSave.set 'commitMessage', commitMessage + else modelToSave = new Patch() modelToSave.set 'delta', @model.getDelta() @@ -151,16 +161,21 @@ module.exports = class I18NEditModelView extends RootView 'collection': _.string.underscored @model.constructor.className 'id': @model.id } + modelToSave.set 'commitMessage', commitMessage - if @modelClass.schema.properties.commitMessage - commitMessage = "Diplomat submission for lang #{@selectedLanguage}: #{flattened.length} change(s)." - modelToSave.set 'commitMessage', commitMessage - errors = modelToSave.validate() button = $(e.target) button.attr('disabled', 'disabled') return button.text('Failed to Submit Changes') if errors - res = modelToSave.save() + type = 'PUT' + if @modelClass.schema.properties.version or (not save) + # Override PUT so we can trigger postNewVersion logic + # or you're POSTing a Patch + type = 'POST' + res = modelToSave.save(null, {type: type}) return button.text('Failed to Submit Changes') unless res + button.text('Submitting...') res.error => button.text('Error Submitting Changes') - res.success => button.text('Submit Changes') + res.success => + @savedBefore = true + button.text('Submit Changes') diff --git a/app/views/i18n/I18NEditPollView.coffee b/app/views/i18n/I18NEditPollView.coffee new file mode 100644 index 000000000..f1e71fda9 --- /dev/null +++ b/app/views/i18n/I18NEditPollView.coffee @@ -0,0 +1,21 @@ +I18NEditModelView = require './I18NEditModelView' +Poll = require 'models/Poll' + +module.exports = class I18NEditPollView extends I18NEditModelView + id: "i18n-edit-poll-view" + modelClass: Poll + + buildTranslationList: -> + lang = @selectedLanguage + + # name, description + if i18n = @model.get('i18n') + if name = @model.get('name') + @wrapRow "Poll name", ['name'], name, i18n[lang]?.name, [] + if description = @model.get('description') + @wrapRow "Poll description", ['description'], description, i18n[lang]?.description, [] + + # answers + for answer, index in @model.get('answers') ? [] + if i18n = answer.i18n + @wrapRow 'Answer', ['text'], answer.text, i18n[lang]?.text, ['answers', index] diff --git a/app/views/i18n/I18NEditThangTypeView.coffee b/app/views/i18n/I18NEditThangTypeView.coffee index 1c3c07012..52c058e9b 100644 --- a/app/views/i18n/I18NEditThangTypeView.coffee +++ b/app/views/i18n/I18NEditThangTypeView.coffee @@ -2,7 +2,7 @@ I18NEditModelView = require './I18NEditModelView' ThangType = require 'models/ThangType' module.exports = class ThangTypeI18NView extends I18NEditModelView - id: "thang-type-i18n-view" + id: 'thang-type-i18n-view' modelClass: ThangType buildTranslationList: -> @@ -13,5 +13,7 @@ module.exports = class ThangTypeI18NView extends I18NEditModelView name = @model.get('name') @wrapRow('Name', ['name'], name, i18n[lang]?.name, []) @wrapRow('Description', ['description'], @model.get('description'), i18n[lang]?.description, [], 'markdown') - @wrapRow('Extended Hero Name', ['extendedName'], @model.get('extendedName'), i18n[lang]?.extendedName, []) - @wrapRow('Unlock Level Name', ['unlockLevelName'], @model.get('unlockLevelName'), i18n[lang]?.unlockLevelName, []) + if extendedName = @model.get('extendedName') + @wrapRow('Extended Hero Name', ['extendedName'], extendedName, i18n[lang]?.extendedName, []) + if unlockLevelName = @model.get('unlockLevelName') + @wrapRow('Unlock Level Name', ['unlockLevelName'], unlockLevelName, i18n[lang]?.unlockLevelName, []) diff --git a/app/views/i18n/I18NHomeView.coffee b/app/views/i18n/I18NHomeView.coffee index 700fdab5b..f4e05d063 100644 --- a/app/views/i18n/I18NHomeView.coffee +++ b/app/views/i18n/I18NHomeView.coffee @@ -6,12 +6,14 @@ LevelComponent = require 'models/LevelComponent' ThangType = require 'models/ThangType' Level = require 'models/Level' Achievement = require 'models/Achievement' +Campaign = require 'models/Campaign' +Poll = require 'models/Poll' languages = _.keys(require 'locale/locale').sort() PAGE_SIZE = 100 module.exports = class I18NHomeView extends RootView - id: "i18n-home-view" + id: 'i18n-home-view' template: template events: @@ -27,15 +29,17 @@ module.exports = class I18NHomeView extends RootView return 2 if m.specificallyCovered return 1 if m.generallyCovered return 0 - - project = ['name', 'components.original', 'i18nCoverage', 'slug'] + + project = ['name', 'components.original', 'i18n', 'i18nCoverage', 'slug'] @thangTypes = new CocoCollection([], { url: '/db/thang.type?view=i18n-coverage', project: project, model: ThangType }) @components = new CocoCollection([], { url: '/db/level.component?view=i18n-coverage', project: project, model: LevelComponent }) @levels = new CocoCollection([], { url: '/db/level?view=i18n-coverage', project: project, model: Level }) @achievements = new CocoCollection([], { url: '/db/achievement?view=i18n-coverage', project: project, model: Achievement }) + @campaigns = new CocoCollection([], { url: '/db/campaign?view=i18n-coverage', project: project, model: Campaign }) + @polls = new CocoCollection([], { url: '/db/poll?view=i18n-coverage', project: project, model: Poll }) - for c in [@thangTypes, @components, @levels, @achievements] + for c in [@thangTypes, @components, @levels, @achievements, @campaigns, @polls] c.skip = 0 c.fetch({data: {skip: 0, limit: PAGE_SIZE}, cache:false}) @supermodel.loadCollection(c, 'documents') @@ -45,10 +49,12 @@ module.exports = class I18NHomeView extends RootView onCollectionSynced: (collection) -> for model in collection.models model.i18nURLBase = switch model.constructor.className - when "ThangType" then "/i18n/thang/" - when "LevelComponent" then "/i18n/component/" - when "Achievement" then "/i18n/achievement/" - when "Level" then "/i18n/level/" + when 'ThangType' then '/i18n/thang/' + when 'LevelComponent' then '/i18n/component/' + when 'Achievement' then '/i18n/achievement/' + when 'Level' then '/i18n/level/' + when 'Campaign' then '/i18n/campaign/' + when 'Poll' then '/i18n/poll/' getMore = collection.models.length is PAGE_SIZE @aggregateModels.add(collection.models) @render() @@ -63,21 +69,22 @@ module.exports = class I18NHomeView extends RootView c.languages = languages c.selectedLanguage = @selectedLanguage c.collection = @aggregateModels - + covered = (m for m in @aggregateModels.models when m.specificallyCovered).length total = @aggregateModels.models.length c.progress = if total then parseInt(100 * covered / total) else 100 - + c.showGeneralCoverage = /-/.test(@selectedLanguage ? 'en') # Only relevant for languages with more than one family, like zh-HANS + c updateCoverage: -> selectedBase = @selectedLanguage[..2] - relatedLanguages = (l for l in languages when l.startsWith(selectedBase) and l isnt @selectedLanguage) + relatedLanguages = (l for l in languages when _.string.startsWith(l, selectedBase) and l isnt @selectedLanguage) for model in @aggregateModels.models @updateCoverageForModel(model, relatedLanguages) - model.generallyCovered = true if @selectedLanguage.startsWith 'en' + model.generallyCovered = true if _.string.startsWith @selectedLanguage, 'en' @aggregateModels.sort() - + updateCoverageForModel: (model, relatedLanguages) -> model.specificallyCovered = true model.generallyCovered = true diff --git a/app/views/play/ladder/LadderPlayModal.coffee b/app/views/ladder/LadderPlayModal.coffee similarity index 94% rename from app/views/play/ladder/LadderPlayModal.coffee rename to app/views/ladder/LadderPlayModal.coffee index 08d9516c7..d0c14ba17 100644 --- a/app/views/play/ladder/LadderPlayModal.coffee +++ b/app/views/ladder/LadderPlayModal.coffee @@ -27,7 +27,7 @@ module.exports = class LadderPlayModal extends ModalView constructor: (options, @level, @session, @team) -> super(options) @nameMap = {} - @otherTeam = if team is 'ogres' then 'humans' else 'ogres' + @otherTeam = if @team is 'ogres' then 'humans' else 'ogres' @startLoadingChallengersMaybe() @wizardType = ThangType.loadUniversalWizard() @@ -37,6 +37,9 @@ module.exports = class LadderPlayModal extends ModalView aceConfig.language = @$el.find('#tome-language').val() me.set 'aceConfig', aceConfig me.patch() + if @session + @session.set 'codeLanguage', aceConfig.language + @session.patch() # PART 1: Load challengers from the db unless some are in the matches startLoadingChallengersMaybe: -> @@ -88,12 +91,13 @@ module.exports = class LadderPlayModal extends ModalView ctx.teamID = @team ctx.otherTeamID = @otherTeam ctx.tutorialLevelExists = @tutorialLevelExists + ctx.language = @session?.get('codeLanguage') ? me.get('aceConfig')?.language ? 'python' ctx.languages = [ {id: 'python', name: 'Python'} {id: 'javascript', name: 'JavaScript'} - {id: 'coffeescript', name: 'CoffeeScript'} + {id: 'coffeescript', name: 'CoffeeScript (Experimental)'} {id: 'clojure', name: 'Clojure (Experimental)'} - {id: 'lua', name: 'Lua (Experimental)'} + {id: 'lua', name: 'Lua'} {id: 'io', name: 'Io (Experimental)'} ] teamsList = teamDataFromLevel @level @@ -195,13 +199,13 @@ class ChallengersData _.extend @, Backbone.Events score = @session?.get('totalScore') or 25 @easyPlayer = new LeaderboardCollection(@level, {order: 1, scoreOffset: score - 5, limit: 1, team: @otherTeam}) - @easyPlayer.fetch() + @easyPlayer.fetch cache: false @listenToOnce(@easyPlayer, 'sync', @challengerLoaded) @mediumPlayer = new LeaderboardCollection(@level, {order: 1, scoreOffset: score, limit: 1, team: @otherTeam}) - @mediumPlayer.fetch() + @mediumPlayer.fetch cache: false @listenToOnce(@mediumPlayer, 'sync', @challengerLoaded) @hardPlayer = new LeaderboardCollection(@level, {order: -1, scoreOffset: score + 5, limit: 1, team: @otherTeam}) - @hardPlayer.fetch() + @hardPlayer.fetch cache: false @listenToOnce(@hardPlayer, 'sync', @challengerLoaded) challengerLoaded: -> diff --git a/app/views/play/ladder/LadderTabView.coffee b/app/views/ladder/LadderTabView.coffee similarity index 90% rename from app/views/play/ladder/LadderTabView.coffee rename to app/views/ladder/LadderTabView.coffee index ea9c3f026..eda4ad279 100644 --- a/app/views/play/ladder/LadderTabView.coffee +++ b/app/views/ladder/LadderTabView.coffee @@ -7,17 +7,19 @@ User = require 'models/User' LeaderboardCollection = require 'collections/LeaderboardCollection' {teamDataFromLevel} = require './utils' ModelModal = require 'views/modal/ModelModal' +require 'vendor/d3' HIGHEST_SCORE = 1000000 module.exports = class LadderTabView extends CocoView id: 'ladder-tab-view' - template: require 'templates/play/ladder/ladder_tab' + template: require 'templates/play/ladder/ladder-tab-view' events: 'click .connect-facebook': 'onConnectFacebook' 'click .connect-google-plus': 'onConnectGPlus' 'click .name-col-cell': 'onClickPlayerName' + 'click .spectate-cell': 'onClickSpectateCell' 'click .load-more-ladder-entries': 'onLoadMoreLadderEntries' subscriptions: @@ -153,7 +155,7 @@ module.exports = class LadderTabView extends CocoView oldLeaderboard.destroy() teamSession = _.find @sessions.models, (session) -> session.get('team') is team.id @leaderboards[team.id] = new LeaderboardData(@level, team.id, teamSession, @ladderLimit) - @leaderboardRes = @supermodel.addModelResource(@leaderboards[team.id], 'leaderboard', {}, 3) + @leaderboardRes = @supermodel.addModelResource(@leaderboards[team.id], 'leaderboard', {cache: false}, 3) @leaderboardRes.load() render: -> @@ -164,7 +166,7 @@ module.exports = class LadderTabView extends CocoView team = _.find @teams, name: histogramWrapper.data('team-name') histogramData = null $.when( - $.get("/db/level/#{@level.get('slug')}/histogram_data?team=#{team.name.toLowerCase()}", (data) -> histogramData = data) + $.get "/db/level/#{@level.get('slug')}/histogram_data?team=#{team.name.toLowerCase()}", {cache: false}, (data) -> histogramData = data ).then => @generateHistogram(histogramWrapper, histogramData, team.name.toLowerCase()) unless @destroyed @@ -276,6 +278,19 @@ module.exports = class LadderTabView extends CocoView session = new LevelSession _id: row.data 'session-id' @openModalView new ModelModal models: [session, player] + onClickSpectateCell: (e) -> + cell = $(e.target).closest '.spectate-cell' + row = cell.parent() + table = row.closest('table') + wasSelected = cell.hasClass 'selected' + table.find('.spectate-cell.selected').removeClass 'selected' + cell = $(e.target).closest('.spectate-cell').toggleClass 'selected', not wasSelected + sessionID = row.data 'session-id' + teamID = table.data 'team' + @spectateTargets ?= {} + @spectateTargets[teamID] = if wasSelected then null else sessionID + console.log @spectateTargets, cell, row, table + onLoadMoreLadderEntries: (e) -> @ladderLimit ?= 100 @ladderLimit += 100 @@ -293,17 +308,17 @@ module.exports.LeaderboardData = LeaderboardData = class LeaderboardData extends console.warn 'Already have top players on', @ if @topPlayers @topPlayers = new LeaderboardCollection(@level, {order: -1, scoreOffset: HIGHEST_SCORE, team: @team, limit: @limit}) promises = [] - promises.push @topPlayers.fetch() + promises.push @topPlayers.fetch cache: false if @session score = @session.get('totalScore') or 10 @playersAbove = new LeaderboardCollection(@level, {order: 1, scoreOffset: score, limit: 4, team: @team}) - promises.push @playersAbove.fetch() + promises.push @playersAbove.fetch cache: false @playersBelow = new LeaderboardCollection(@level, {order: -1, scoreOffset: score, limit: 4, team: @team}) - promises.push @playersBelow.fetch() + promises.push @playersBelow.fetch cache: false level = "#{@level.get('original')}.#{@level.get('version').major}" success = (@myRank) => - promises.push $.ajax "/db/level/#{level}/leaderboard_rank?scoreOffset=#{@session.get('totalScore')}&team=#{@team}", {success} + promises.push $.ajax("/db/level/#{level}/leaderboard_rank?scoreOffset=#{@session.get('totalScore')}&team=#{@team}", cache: false, success: success) @promise = $.when(promises...) @promise.then @onLoad @promise.fail @onFail diff --git a/app/views/play/ladder/LadderView.coffee b/app/views/ladder/LadderView.coffee similarity index 73% rename from app/views/play/ladder/LadderView.coffee rename to app/views/ladder/LadderView.coffee index 03b34a0eb..0c13316cd 100644 --- a/app/views/play/ladder/LadderView.coffee +++ b/app/views/ladder/LadderView.coffee @@ -33,11 +33,12 @@ module.exports = class LadderView extends RootView events: 'click .play-button': 'onClickPlayButton' 'click a:not([data-toggle])': 'onClickedLink' + 'click .spectate-button': 'onClickSpectateButton' constructor: (options, @levelID) -> super(options) @level = @supermodel.loadModel(new Level(_id: @levelID), 'level').model - @sessions = @supermodel.loadCollection(new LevelSessionsCollection(levelID), 'your_sessions').model + @sessions = @supermodel.loadCollection(new LevelSessionsCollection(@levelID), 'your_sessions', {cache: false}).model @teams = [] @@ -53,9 +54,11 @@ module.exports = class LadderView extends RootView ctx.levelID = @levelID ctx.levelDescription = marked(@level.get('description')) if @level.get('description') ctx._ = _ - if tournamentDate = {greed: 1402444800000, 'criss-cross': 1410912000000}[@levelID] - ctx.tournamentTimeLeft = moment(new Date(tournamentDate)).fromNow() - ctx.winners = require('views/play/ladder/tournament_results')[@levelID] + if tournamentEndDate = {greed: 1402444800000, 'criss-cross': 1410912000000, 'zero-sum': 1428364800000}[@levelID] + ctx.tournamentTimeLeft = moment(new Date(tournamentEndDate)).fromNow() + if tournamentStartDate = {'zero-sum': 1427472000000}[@levelID] + ctx.tournamentTimeElapsed = moment(new Date(tournamentStartDate)).fromNow() + ctx.winners = require('./tournament_results')[@levelID] ctx afterRender: -> @@ -64,14 +67,14 @@ module.exports = class LadderView extends RootView @insertSubView(@ladderTab = new LadderTabView({}, @level, @sessions)) @insertSubView(@myMatchesTab = new MyMatchesTabView({}, @level, @sessions)) @insertSubView(@simulateTab = new SimulateTabView()) - @refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 20 * 1000) + @refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 60 * 1000) hash = document.location.hash[1..] if document.location.hash if hash and not (hash in ['my-matches', 'simulate', 'ladder', 'prizes', 'rules', 'winners']) @showPlayModal(hash) if @sessions.loaded fetchSessionsAndRefreshViews: -> return if @destroyed or application.userIsIdle or (new Date() - 2000 < @lastRefreshTime) or not @supermodel.finished() - @sessions.fetch({'success': @refreshViews}) + @sessions.fetch success: @refreshViews, cache: false refreshViews: => return if @destroyed or application.userIsIdle @@ -86,26 +89,29 @@ module.exports = class LadderView extends RootView onClickPlayButton: (e) -> @showPlayModal($(e.target).closest('.play-button').data('team')) + onClickSpectateButton: (e) -> + humanSession = @ladderTab.spectateTargets?.humans + ogreSession = @ladderTab.spectateTargets?.ogres + console.log humanSession, ogreSession + return unless humanSession and ogreSession + e.preventDefault() + e.stopImmediatePropagation() + url = "/play/spectate/#{@level.get('slug')}?session-one=#{humanSession}&session-two=#{ogreSession}" + Backbone.Mediator.publish 'router:navigate', route: url + showPlayModal: (teamID) -> - return @showApologeticSignupModal() if me.get('anonymous') session = (s for s in @sessions.models when s.get('team') is teamID)[0] modal = new LadderPlayModal({}, @level, session, teamID) @openModalView modal - showApologeticSignupModal: -> - AuthModal = require 'views/core/AuthModal' - @openModalView(new AuthModal({showRequiredError: true})) - onClickedLink: (e) -> link = $(e.target).closest('a').attr('href') - if link?.startsWith('/play/level') and me.get('anonymous') - e.stopPropagation() - e.preventDefault() - @showApologeticSignupModal() if link and /#rules$/.test link @$el.find('a[href="#rules"]').tab('show') if link and /#prizes/.test link @$el.find('a[href="#prizes"]').tab('show') + if link and /#winners/.test link + @$el.find('a[href="#winners"]').tab('show') destroy: -> clearInterval @refreshInterval diff --git a/app/views/play/ladder/MainLadderView.coffee b/app/views/ladder/MainLadderView.coffee similarity index 70% rename from app/views/play/ladder/MainLadderView.coffee rename to app/views/ladder/MainLadderView.coffee index 20a200126..6f1349766 100644 --- a/app/views/play/ladder/MainLadderView.coffee +++ b/app/views/ladder/MainLadderView.coffee @@ -19,7 +19,7 @@ module.exports = class LadderHomeView extends RootView super options @levelStatusMap = {} @levelPlayCountMap = {} - @sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model + @sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', {cache: false}, 0).model @listenToOnce @sessions, 'sync', @onSessionsLoaded @getLevelPlayCounts() @@ -54,8 +54,45 @@ module.exports = class LadderHomeView extends RootView context.campaigns = campaigns context +heroArenas = [ + { + name: 'Zero Sum' + difficulty: 3 + id: 'zero-sum' + image: '/file/db/level/550363b4ec31df9c691ab629/MAR26-Banner_Zero%20Sum.png' + description: 'Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer.' + } + { + name: 'Cavern Survival' + difficulty: 1 + id: 'cavern-survival' + image: '' + description: 'Stay alive longer than your multiplayer opponent amidst hordes of ogres!' + } + { + name: 'Dueling Grounds' + difficulty: 1 + id: 'dueling-grounds' + image: '' + description: 'Battle head-to-head against another hero in this basic beginner combat arena.' + } + { + name: 'Multiplayer Treasure Grove' + difficulty: 2 + id: 'multiplayer-treasure-grove' + image: '' + description: 'Mix collection, flags, and combat in this multiplayer coin-gathering arena.' + } + { + name: 'Harrowland' + difficulty: 2 + id: 'harrowland' + image: '' + description: 'Go head-to-head against another player in this dueling arena--but watch out for their friends!' + } +] -arenas = [ +oldArenas = [ { name: 'Criss-Cross' difficulty: 5 @@ -101,5 +138,6 @@ arenas = [ ] campaigns = [ - {id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: arenas} + {id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: heroArenas} + #{id: 'old_multiplayer', name: '(Deprecated) Old Multiplayer Arenas', description: 'Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas.', levels: oldArenas} ] diff --git a/app/views/play/ladder/MyMatchesTabView.coffee b/app/views/ladder/MyMatchesTabView.coffee similarity index 88% rename from app/views/play/ladder/MyMatchesTabView.coffee rename to app/views/ladder/MyMatchesTabView.coffee index 011b99779..b03b87726 100644 --- a/app/views/play/ladder/MyMatchesTabView.coffee +++ b/app/views/ladder/MyMatchesTabView.coffee @@ -4,6 +4,7 @@ LevelSession = require 'models/LevelSession' LeaderboardCollection = require 'collections/LeaderboardCollection' LadderSubmissionView = require 'views/play/common/LadderSubmissionView' {teamDataFromLevel} = require './utils' +require 'vendor/d3' module.exports = class MyMatchesTabView extends CocoView id: 'my-matches-tab-view' @@ -30,6 +31,7 @@ module.exports = class MyMatchesTabView extends CocoView continue ids.push id unless @nameMap[id] + ids = _.uniq ids return unless ids.length success = (nameMap) => @@ -37,7 +39,17 @@ module.exports = class MyMatchesTabView extends CocoView for session in @sessions.models for match in session.get('matches') or [] opponent = match.opponents[0] - @nameMap[opponent.userID] ?= nameMap[opponent.userID]?.name ? '<bad match data>' + continue if @nameMap[opponent.userID] + opponentUser = nameMap[opponent.userID] + name = opponentUser?.name + name ||= opponentUser.firstName + ' ' + opponentUser.lastName if opponentUser?.firstName + name ||= "Anonymous #{opponent.userID.substr(18)}" if opponentUser + unless name + console.log 'found', nameMap[opponent.userID], 'for', opponent.userID, "http://codecombat.com/db/user/#{opponent.userID}" + name ||= '<bad match data>' + if name.length > 21 + name = name.substr(0, 18) + '...' + @nameMap[opponent.userID] = name @render() if @supermodel.finished() userNamesRequest = @supermodel.addRequestResource 'user_names', { @@ -71,6 +83,7 @@ module.exports = class MyMatchesTabView extends CocoView stale: match.date < submitDate fresh: fresh codeLanguage: match.codeLanguage + simulator: JSON.stringify(match.simulator) + ' | seed ' + match.randomSeed } for team in @teams diff --git a/app/views/play/ladder/SimulateTabView.coffee b/app/views/ladder/SimulateTabView.coffee similarity index 81% rename from app/views/play/ladder/SimulateTabView.coffee rename to app/views/ladder/SimulateTabView.coffee index b6a52c659..1fc3ab3b2 100644 --- a/app/views/play/ladder/SimulateTabView.coffee +++ b/app/views/ladder/SimulateTabView.coffee @@ -15,12 +15,20 @@ module.exports = class SimulateTabView extends CocoView constructor: (options) -> super(options) @simulatorsLeaderboardData = new SimulatorsLeaderboardData(me) - @simulatorsLeaderboardDataRes = @supermodel.addModelResource(@simulatorsLeaderboardData, 'top_simulators') + @simulatorsLeaderboardDataRes = @supermodel.addModelResource(@simulatorsLeaderboardData, 'top_simulators', {cache: false}) @simulatorsLeaderboardDataRes.load() + require 'vendor/aether-javascript' + require 'vendor/aether-python' + require 'vendor/aether-coffeescript' + require 'vendor/aether-lua' + require 'vendor/aether-clojure' + require 'vendor/aether-io' onLoaded: -> super() @render() + if document.location.hash is '#simulate' and not @simulator + @startSimulating() getRenderData: -> ctx = super() @@ -36,10 +44,19 @@ module.exports = class SimulateTabView extends CocoView # Simulations onSimulateButtonClick: (e) -> - application.tracker?.trackEvent 'Simulate Button Click', {} + application.tracker?.trackEvent 'Simulate Button Click' + @startSimulating() + + startSimulating: -> + @simulationPageRefreshTimeout = _.delay @refreshAndContinueSimulating, 30 * 60 * 1000 + @simulateNextGame() $('#simulate-button').prop 'disabled', true $('#simulate-button').text 'Simulating...' - @simulateNextGame() + + refreshAndContinueSimulating: => + # We refresh the page every now and again to make sure simulations haven't gotten derailed by bogus games, and that simulators don't hang on to old, stale code or data. + document.location.hash = '#simulate' + document.location.reload() simulateNextGame: -> unless @simulator @@ -48,7 +65,7 @@ module.exports = class SimulateTabView extends CocoView # Work around simulator getting super slow on Chrome fetchAndSimulateTaskOriginal = @simulator.fetchAndSimulateTask @simulator.fetchAndSimulateTask = => - if @simulator.simulatedByYou >= 5 + if @simulator.simulatedByYou >= 20 console.log '------------------- Destroying Simulator and making a new one -----------------' @simulator.destroy() @simulator = null @@ -60,7 +77,7 @@ module.exports = class SimulateTabView extends CocoView refresh: -> success = (numberOfGamesInQueue) -> $('#games-in-queue').text numberOfGamesInQueue - $.ajax '/queue/messagesInQueueCount', {success} + $.ajax '/queue/messagesInQueueCount', cache: false, success: success updateSimulationStatus: (simulationStatus, sessions) -> if simulationStatus is 'Fetching simulation data!' @@ -95,7 +112,7 @@ module.exports = class SimulateTabView extends CocoView console.log jqxhr.responseText destroy: -> - clearInterval @refreshInterval + clearTimeout @simulationPageRefreshTimeout @simulator?.destroy() super() @@ -114,15 +131,14 @@ class SimulatorsLeaderboardData extends CocoClass unless @me.get('anonymous') score = @me.get('simulatedBy') or 0 queueSuccess = (@numberOfGamesInQueue) => - promises.push $.ajax '/queue/messagesInQueueCount', {success: queueSuccess} + promises.push $.ajax '/queue/messagesInQueueCount', {success: queueSuccess, cache: false} @playersAbove = new SimulatorsLeaderboardCollection({order: 1, scoreOffset: score, limit: 4}) promises.push @playersAbove.fetch() if score @playersBelow = new SimulatorsLeaderboardCollection({order: -1, scoreOffset: score, limit: 4}) promises.push @playersBelow.fetch() success = (@myRank) => - - promises.push $.ajax "/db/user/me/simulator_leaderboard_rank?scoreOffset=#{score}", {success} + promises.push $.ajax("/db/user/me/simulator_leaderboard_rank?scoreOffset=#{score}", cache: false, success: success) @promise = $.when(promises...) @promise.then @onLoad diff --git a/app/views/play/ladder/tournament_results.coffee b/app/views/ladder/tournament_results.coffee similarity index 73% rename from app/views/play/ladder/tournament_results.coffee rename to app/views/ladder/tournament_results.coffee index 6b4ae28fb..3b20b9a02 100644 --- a/app/views/play/ladder/tournament_results.coffee +++ b/app/views/ladder/tournament_results.coffee @@ -1,4 +1,4 @@ -module.exports = results = greed: {}, 'criss-cross': {} +module.exports = results = greed: {}, 'criss-cross': {}, 'zero-sum': {} results.greed.humans = [ {team: 'humans', rank: 1, sessionID: '5381e3537585483905a829c1', name: 'Wizard Dude', playtime: 63184, wins: 363, losses: 0, score: 363} @@ -746,3 +746,205 @@ results['criss-cross'].ogres = [ {team: 'ogres', rank: 72, sessionID: '53f4847afda22e3305bdbea5', codeLanguage: 'javascript', name: 'CodeMinion', score: 9.706964015} {team: 'ogres', rank: 73, sessionID: '53fba49c3baef834054b98c6', codeLanguage: 'javascript', name: 'Moises Banales', score: 8.991630973} ] + +results['zero-sum'].humans = [ + {team: 'humans', rank: 1, userID: '539b571db4c4f9380545317d', sessionID: '55188bcee3ae3a3505f7d1ad', name: 'NoJuice4u', wins: 105, losses: 3, playtime: 46222, codeLanguage: 'javascript'} + {team: 'humans', rank: 2, userID: '54ef65eaef838a790598b305', sessionID: '5518349d1f12482609b452ea', name: 'Qenf', wins: 99, losses: 9, playtime: 13554, codeLanguage: 'python'} + {team: 'humans', rank: 3, userID: '5308bb6cc326dda10cf62d6d', sessionID: '55172d95e3ae3a3505f7a6ed', name: 'Stofflon', wins: 95, losses: 13, playtime: 22609, codeLanguage: 'javascript'} + {team: 'humans', rank: 3, userID: '539bccfb6e1d92310506ffa9', sessionID: '5518d8624c73053505254062', name: 'Driphter', wins: 95, losses: 13, playtime: 32555, codeLanguage: 'javascript'} + {team: 'humans', rank: 5, userID: '54d2c6ac3e16915505f0cef3', sessionID: '551f2d159800ae33059c5052', name: 'KingCode', wins: 93, losses: 15, playtime: 13510, codeLanguage: 'javascript'} + {team: 'humans', rank: 5, userID: '549ee5208f3c153d0518ff24', sessionID: '55195098d69ccd3305a2ea0c', name: 'wabu', wins: 93, losses: 15, playtime: 3274, codeLanguage: 'python'} + {team: 'humans', rank: 5, userID: '5500505220b2567d0514bab9', sessionID: '551582ecf43d375105fbdf00', name: 'Muadibi', wins: 93, losses: 15, playtime: 30381, codeLanguage: 'javascript'} + {team: 'humans', rank: 5, userID: '54f015a0df1b7d7d05610870', sessionID: '55172f2ec13fa03205248953', name: 'Andre95', wins: 93, losses: 15, playtime: 53158, codeLanguage: 'python'} + {team: 'humans', rank: 9, userID: '54ff548501c724b9072f9f6d', sessionID: '551bd2f91794dc1a112bfc28', name: 'mAsTer_A', wins: 91, losses: 16, playtime: 11860, codeLanguage: 'javascript'} + {team: 'humans', rank: 10, userID: '539c13ba6e1d923105072c32', sessionID: '5515a210d792f354050c9339', name: 'rasdasd', wins: 89, losses: 19, playtime: 12710, codeLanguage: 'javascript'} + {team: 'humans', rank: 11, userID: '5516520db77a16330597d9a3', sessionID: '55165233de8bc03205975972', name: 'SlamBlasta', wins: 88, losses: 20, playtime: 10063, codeLanguage: 'python'} + {team: 'humans', rank: 12, userID: '512ef4805a67a8c507000001', sessionID: '550370aeec31df9c691ab632', name: 'Nick', wins: 87, losses: 20, playtime: 22856, codeLanguage: 'python'} + {team: 'humans', rank: 13, userID: '55164512de8bc032059758dc', sessionID: '55184ea8e3ae3a3505f7c811', name: 'Geoboardman', wins: 87, losses: 21, playtime: 11451, codeLanguage: 'python'} + {team: 'humans', rank: 13, userID: '551750ca81389437054def79', sessionID: '5517545fe3ae3a3505f7ac1f', name: 'the real Carl Maxwell', wins: 87, losses: 21, playtime: 6242, codeLanguage: 'python'} + {team: 'humans', rank: 15, userID: '537d01f484c54c6e05c05989', sessionID: '55164e9f4e83223605e7942a', name: 'Serg', wins: 85, losses: 23, playtime: 8176, codeLanguage: 'javascript'} + {team: 'humans', rank: 16, userID: '5478a26cc439b9160865b3b3', sessionID: '550ceb075834237a05acda6e', name: 'Tutor Delphia', wins: 83, losses: 25, playtime: 30978, codeLanguage: 'javascript'} + {team: 'humans', rank: 17, userID: '551d78734c73053505267238', sessionID: '551dc7046cc7da101fe5fe52', name: 'Kongou', wins: 79, losses: 29, playtime: 561, codeLanguage: 'python'} + {team: 'humans', rank: 18, userID: '53f4cb0cfda22e3305be24e7', sessionID: '5515ecc4b8185a5205e1a304', name: 'Dothgar', wins: 74, losses: 34, playtime: 4129, codeLanguage: 'javascript'} + {team: 'humans', rank: 19, userID: '5520a4caabdb444805e85da1', sessionID: '5522f7ae801bea3905cb6fd9', name: 'Bazhan', wins: 73, losses: 34, playtime: 4466, codeLanguage: 'python'} + {team: 'humans', rank: 20, userID: '5506e39e84ce5b7905627e41', sessionID: '551bed505afdf532055862e4', name: 'pixx', wins: 73, losses: 35, playtime: 12182, codeLanguage: 'javascript'} + {team: 'humans', rank: 21, userID: '5516aa174d24212f05944d19', sessionID: '5517065858852c3405f48e9d', name: 'Haruna', wins: 70, losses: 38, playtime: 19377, codeLanguage: 'javascript'} + {team: 'humans', rank: 22, userID: '54a800d3a487d53f056c0bb7', sessionID: '5519bac15cd6bae40951f460', name: 'Mensian', wins: 69, losses: 39, playtime: 17817, codeLanguage: 'javascript'} + {team: 'humans', rank: 23, userID: '5519d8885afdf5320557df5c', sessionID: '5519d88e8328b9020debf019', name: 'StabGun', wins: 69, losses: 39, playtime: 3710, codeLanguage: 'python'} + {team: 'humans', rank: 23, userID: '55187c2c5afdf532055782df', sessionID: '55187c63d69ccd3305a2c33b', name: 'shakesoda', wins: 69, losses: 39, playtime: 19789, codeLanguage: 'lua'} + {team: 'humans', rank: 25, userID: '5429a6d19f9fa2bf2c5a4e4e', sessionID: '55173873d69ccd3305a2918c', name: 'FadingSun32', wins: 65, losses: 43, playtime: 113, codeLanguage: 'javascript'} + {team: 'humans', rank: 25, userID: '54f9e344d12cf54d069d92dc', sessionID: '550d0ba29e22dc56056be1dd', name: 'xianjin', wins: 65, losses: 43, playtime: 693, codeLanguage: 'python'} + {team: 'humans', rank: 27, userID: '54fd36429ae7221e06728f3b', sessionID: '550d001c9e22dc56056be0d8', name: 'Jasmine49', wins: 59, losses: 49, playtime: 5222, codeLanguage: 'python'} + {team: 'humans', rank: 27, userID: '551b205cc13fa03205254f3e', sessionID: '551c04088328b9020dec9ef1', name: 'my98olds', wins: 59, losses: 49, playtime: 22943, codeLanguage: 'javascript'} + {team: 'humans', rank: 29, userID: '54bbb1016ef7bdde07101c4e', sessionID: '551807d0c13fa03205249b3d', name: 'PSL', wins: 57, losses: 51, playtime: 2523, codeLanguage: 'javascript'} + {team: 'humans', rank: 30, userID: '539ea2ba64edb6fa0bf584f7', sessionID: '5516a002b77a16330597e208', name: 'MarS explorer', wins: 55, losses: 53, playtime: 5966, codeLanguage: 'lua'} + {team: 'humans', rank: 30, userID: '548e1d2d24446d3d050281ed', sessionID: '55080c49039bafd50cb92fae', name: 'J_F_B_M', wins: 55, losses: 53, playtime: 7951, codeLanguage: 'javascript'} + {team: 'humans', rank: 32, userID: '5489342042443b56076a1ad8', sessionID: '55173465e3ae3a3505f7a7ff', name: 'AnnieHall', wins: 54, losses: 52, playtime: 5576, codeLanguage: 'python'} + {team: 'humans', rank: 33, userID: '54f1ddf248724e7d052b75fd', sessionID: '551a7f0e5cd6bae409522148', name: 'TiboW', wins: 54, losses: 54, playtime: 942, codeLanguage: 'python'} + {team: 'humans', rank: 34, userID: '55169c96aedec33205e8acfc', sessionID: '55169df0aedec33205e8ad47', name: 'funny_falcon', wins: 51, losses: 57, playtime: 4486, codeLanguage: 'python'} + {team: 'humans', rank: 35, userID: '54feee1ae950f7a707ec3461', sessionID: '5515d0933bdee5954e8246df', name: 'Khorrok', wins: 50, losses: 57, playtime: 387, codeLanguage: 'python'} + {team: 'humans', rank: 36, userID: '54f7e04e8ec032e705abbcd8', sessionID: '550cc8eba605ba5a05120514', name: 'Tan Chuan Jie', wins: 50, losses: 58, playtime: 4787, codeLanguage: 'python'} + {team: 'humans', rank: 36, userID: '551743df079f2833053c723a', sessionID: '5517582bd69ccd3305a29676', name: 'Majestik', wins: 50, losses: 58, playtime: 15118, codeLanguage: 'javascript'} + {team: 'humans', rank: 38, userID: '55005eb295a4228105f6bfc3', sessionID: '550d0e699e22dc56056be203', name: '2SHAWN4YOU', wins: 49, losses: 59, playtime: 935, codeLanguage: 'python'} + {team: 'humans', rank: 39, userID: '54dcf4ec7b4e13500586e511', sessionID: '5518609b5afdf53205577d82', name: 'Da5id', wins: 46, losses: 61, playtime: 711, codeLanguage: 'python'} + {team: 'humans', rank: 40, userID: '550420ad2c5fbf7b056a18a0', sessionID: '550cffa5a605ba5a051205d4', name: 'YuanFeng', wins: 46, losses: 62, playtime: 5471, codeLanguage: 'python'} + {team: 'humans', rank: 41, userID: '54f9cad2d12cf54d069d8465', sessionID: '550cc77d9e22dc56056bdfe8', name: 'abcdefguan', wins: 45, losses: 61, playtime: 4873, codeLanguage: 'python'} + {team: 'humans', rank: 42, userID: '54f914455cb508f3058b6553', sessionID: '550d00759e22dc56056be0e0', name: 'KaiXFlare', wins: 45, losses: 63, playtime: 5578, codeLanguage: 'python'} + {team: 'humans', rank: 42, userID: '5443c3609792f94d06eecbb3', sessionID: '5515afd91c3bb18709e26474', name: 'PiedotTaste', wins: 45, losses: 63, playtime: 503, codeLanguage: 'javascript'} + {team: 'humans', rank: 44, userID: '53b9fa0194abf748058ea71a', sessionID: '55160678b77a16330597d2ea', name: 'UMD_Liquidator', wins: 44, losses: 64, playtime: 16710, codeLanguage: 'javascript'} + {team: 'humans', rank: 45, userID: '550db4c425b6f688062cb7f5', sessionID: '5515bc21fd67e4b4095aae6e', name: 'coderg0d', wins: 43, losses: 65, playtime: 17150, codeLanguage: 'javascript'} + {team: 'humans', rank: 46, userID: '53e5783d6c59f5340504a6e9', sessionID: '55162c77de8bc03205975793', name: 'YodaEater', wins: 42, losses: 66, playtime: 15075, codeLanguage: 'python'} + {team: 'humans', rank: 47, userID: '539cec01ab5dfc3a05cbf2d2', sessionID: '55158457b8185a5205e172a7', name: 'matthewd', wins: 41, losses: 67, playtime: 2166, codeLanguage: 'javascript'} + {team: 'humans', rank: 48, userID: '535d52e90f77a8470b782df4', sessionID: '551676f64d24212f059446b6', name: 'binarychase', wins: 40, losses: 68, playtime: 13148, codeLanguage: 'javascript'} + {team: 'humans', rank: 49, userID: '54f9064ad12cf54d069d54a4', sessionID: '550cc7469e22dc56056bdfe3', name: 'hussain1998', wins: 39, losses: 69, playtime: 5516, codeLanguage: 'python'} + {team: 'humans', rank: 49, userID: '539bb5b7ab5dfc3a05cae063', sessionID: '55216e3885fca531055424d6', name: 'DollarAkshay', wins: 39, losses: 69, playtime: 965, codeLanguage: 'javascript'} + {team: 'humans', rank: 49, userID: '54cf001ead25d155057ec294', sessionID: '550cc77c9e22dc56056bdfe7', name: 'jasmineseah-17', wins: 39, losses: 69, playtime: 5260, codeLanguage: 'python'} + {team: 'humans', rank: 52, userID: '55159c561a93145605a65360', sessionID: '55159cde99d51b55053609b4', name: 'PaulT', wins: 37, losses: 71, playtime: 2635, codeLanguage: 'python'} + {team: 'humans', rank: 53, userID: '54ce3426c39305530570368c', sessionID: '5516a6c74d24212f05944ca9', name: 'Zenador', wins: 36, losses: 70, playtime: 6894, codeLanguage: 'javascript'} + {team: 'humans', rank: 54, userID: '550493e2836beb81054ad1c2', sessionID: '551822bc81389437054e089d', name: 'MrWooly', wins: 36, losses: 72, playtime: 440, codeLanguage: 'python'} + {team: 'humans', rank: 54, userID: '5501d57b3d219b9808c098e9', sessionID: '5517f863079f2833053c8413', name: 'Mouschti', wins: 36, losses: 72, playtime: 532, codeLanguage: 'javascript'} + {team: 'humans', rank: 54, userID: '535a3dd5f3c9943f0a4ed434', sessionID: '551f45faed37b43005e2c85d', name: 'Chankorinman', wins: 36, losses: 72, playtime: 581, codeLanguage: 'javascript'} + {team: 'humans', rank: 54, userID: '55156d00d792f354050c61ea', sessionID: '5519fe8c5afdf5320557e679', name: 'Pavel87', wins: 36, losses: 72, playtime: 1900, codeLanguage: 'python'} + {team: 'humans', rank: 54, userID: '5305df9df269d92647ceb862', sessionID: '551a81c2c13fa03205251429', name: 'TimTam', wins: 36, losses: 72, playtime: 1773, codeLanguage: 'python'} + {team: 'humans', rank: 59, userID: '54be1a95cfb4e9ab16128314', sessionID: '5516e7f758852c3405f487d2', name: 'Albertos', wins: 35, losses: 73, playtime: 335, codeLanguage: 'python'} + {team: 'humans', rank: 59, userID: '54dddee006024d52057534a0', sessionID: '551a4d835cd6bae409521577', name: 'ryu.dy', wins: 35, losses: 73, playtime: 4247, codeLanguage: 'python'} + {team: 'humans', rank: 61, userID: '548cf2d0457ef12c0922ce04', sessionID: '551680efc79ef1fd062b57df', name: 'Lee87', wins: 34, losses: 74, playtime: 243, codeLanguage: 'javascript'} + {team: 'humans', rank: 61, userID: '54f00e7cbc22bc830591b375', sessionID: '55222ad6d06c12300505737e', name: 'Fame7', wins: 34, losses: 74, playtime: 880, codeLanguage: 'python'} + {team: 'humans', rank: 61, userID: '527ba37865a2cf8339000efd', sessionID: '5516ea7758852c3405f48871', name: 'Elinus', wins: 34, losses: 74, playtime: 75, codeLanguage: 'javascript'} + {team: 'humans', rank: 61, userID: '54b87f637755bf5405a600f7', sessionID: '551dfedc4c73053505269424', name: 'Anonymous', wins: 34, losses: 74, playtime: 531, codeLanguage: 'javascript'} + {team: 'humans', rank: 61, userID: '55127452ee12f7580524512f', sessionID: '551c83fb4c73053505263a4e', name: 'Oxidis', wins: 34, losses: 74, playtime: 1526, codeLanguage: 'python'} + {team: 'humans', rank: 61, userID: '54f94d77802189c30518184c', sessionID: '550cffdfa605ba5a051205d6', name: 'OblivionOverlord', wins: 34, losses: 74, playtime: 4130, codeLanguage: 'python'} + {team: 'humans', rank: 67, userID: '5515d99c9f2b9c5305f56d5c', sessionID: '551ac6e24c7305350525bb5b', name: 'Anonymous', wins: 32, losses: 76, playtime: 5641, codeLanguage: 'javascript'} + {team: 'humans', rank: 67, userID: '540219a742fb9c380ad1eae3', sessionID: '551618f253665d3005251ae0', name: 'shadowfire', wins: 32, losses: 76, playtime: 1105, codeLanguage: 'javascript'} + {team: 'humans', rank: 69, userID: '5477bd26c439b9160865589f', sessionID: '5518a3a74c73053505253acf', name: 'anodinia', wins: 31, losses: 77, playtime: 231, codeLanguage: 'python'} + {team: 'humans', rank: 69, userID: '53346ca2523566907564fd53', sessionID: '5522edd6c9e6fd33051496ed', name: 'Ksionc', wins: 31, losses: 77, playtime: 289, codeLanguage: 'javascript'} + {team: 'humans', rank: 69, userID: '54f945e463684f2409c7d498', sessionID: '550cffe1a605ba5a051205d7', name: 'ItsBlitz', wins: 31, losses: 77, playtime: 4889, codeLanguage: 'python'} + {team: 'humans', rank: 72, userID: '5513153fad27c0b705c2829e', sessionID: '5516bb70b77a16330597e6e0', name: 'iPick', wins: 30, losses: 78, playtime: 1109, codeLanguage: 'javascript'} + {team: 'humans', rank: 72, userID: '53049f3350b42adb6e86e046', sessionID: '550cfffba605ba5a051205d8', name: 'gph004', wins: 30, losses: 78, playtime: 4679, codeLanguage: 'python'} + {team: 'humans', rank: 74, userID: '536eeed3bf08a63905efdd8a', sessionID: '550c79bca98c9c7f05ab05be', name: 'basicer', wins: 28, losses: 77, playtime: 1199, codeLanguage: 'lua'} + {team: 'humans', rank: 75, userID: '54623591da18079109220097', sessionID: '5518e6c01f12482609b47160', name: 'FreeKrad', wins: 29, losses: 79, playtime: 1384, codeLanguage: 'javascript'} + {team: 'humans', rank: 75, userID: '54cc2cf2f20cb05105c3d036', sessionID: '551bf5b24c73053505260370', name: 'Mihai2015', wins: 29, losses: 79, playtime: 1415, codeLanguage: 'python'} + {team: 'humans', rank: 75, userID: '550fe04ceb13a37c05136498', sessionID: '551ad43f5cd6bae409525096', name: 'FuzzicalLogic', wins: 29, losses: 79, playtime: 88327, codeLanguage: 'javascript'} + {team: 'humans', rank: 75, userID: '52d81ab38133efa92e0000d0', sessionID: '5515a7091a93145605a65bd9', name: 'DamienG002', wins: 29, losses: 79, playtime: 1619, codeLanguage: 'python'} + {team: 'humans', rank: 75, userID: '550161abf94fb5820504d6d9', sessionID: '551eb864d69ccd3305a4b4bd', name: 'Ien Clack', wins: 29, losses: 79, playtime: 610, codeLanguage: 'python'} + {team: 'humans', rank: 75, userID: '55080de2190b7bba08dab219', sessionID: '551f459f9800ae33059c539f', name: 'Anonymous', wins: 29, losses: 79, playtime: 651, codeLanguage: 'python'} + {team: 'humans', rank: 81, userID: '550fc1b5e8cfa180057bcf9a', sessionID: '551dbdb95afdf5320559095b', name: 'Jython', wins: 28, losses: 80, playtime: 965, codeLanguage: 'python'} + {team: 'humans', rank: 81, userID: '551711f88bdcbad0074fb07e', sessionID: '552189d56a8b7c22069934a6', name: 'GJW1', wins: 28, losses: 80, playtime: 320, codeLanguage: 'python'} + {team: 'humans', rank: 81, userID: '5391144f82f0bc470523c8ab', sessionID: '550cc74bca461f5705f3d144', name: 'Polaricicle', wins: 28, losses: 80, playtime: 5801, codeLanguage: 'python'} + {team: 'humans', rank: 84, userID: '548ae4ddaed203880bc9d72d', sessionID: '55213f1c85fca53105541f99', name: 'Brizar', wins: 26, losses: 82, playtime: 1827, codeLanguage: 'python'} + {team: 'humans', rank: 85, userID: '54fc40f847b0892406d7428c', sessionID: '550d004bca461f5705f3d1ed', name: 'galaxypantz', wins: 25, losses: 82, playtime: 3444, codeLanguage: 'python'} + {team: 'humans', rank: 86, userID: '55196a90e3ae3a3505f7fdcd', sessionID: '55196b035afdf5320557b249', name: 'zbx', wins: 24, losses: 84, playtime: 4048, codeLanguage: 'python'} + {team: 'humans', rank: 86, userID: '550d599a03cae479055c02bd', sessionID: '5516a33f58852c3405f47767', name: 'Ben0225', wins: 24, losses: 84, playtime: 6580, codeLanguage: 'python'} + {team: 'humans', rank: 88, userID: '55158827bf2bea55051d0a0a', sessionID: '551da49c5afdf532055901df', name: 'kpbochenek', wins: 22, losses: 82, playtime: 798, codeLanguage: 'python'} + {team: 'humans', rank: 89, userID: '547b41f59325e43e052e02a8', sessionID: '5515c3871a93145605a66b4b', name: 'Tamerlan4', wins: 23, losses: 84, playtime: 196, codeLanguage: 'javascript'} + {team: 'humans', rank: 90, userID: '53880299d06c503805fdd57e', sessionID: '5519aeb2c13fa0320524f401', name: 'Дор', wins: 22, losses: 85, playtime: 177, codeLanguage: 'python'} + {team: 'humans', rank: 91, userID: '550701ea75c0eb4a10ac7f4a', sessionID: '550d00bb9e22dc56056be0e5', name: 'Loh June Yong', wins: 22, losses: 86, playtime: 5443, codeLanguage: 'python'} + {team: 'humans', rank: 92, userID: '52a8f5e7a9fb033114000f08', sessionID: '55158154f43d375105fbdcf8', name: 'Mpgod1234', wins: 21, losses: 86, playtime: 558, codeLanguage: 'javascript'} + {team: 'humans', rank: 93, userID: '546e523a047aaa780854a46b', sessionID: '55087d90e810e35a09e335e8', name: 'Popey456963', wins: 21, losses: 87, playtime: 4724, codeLanguage: 'python'} + {team: 'humans', rank: 94, userID: '54f114d51022997e054ea52b', sessionID: '551d30c8e3ae3a3505f91191', name: 'F_Deity_Link', wins: 20, losses: 87, playtime: 1324, codeLanguage: 'lua'} + {team: 'humans', rank: 94, userID: '5515ffd982ee611b05aeb6f7', sessionID: '5516008aa174c81b05465604', name: 'Anonymous', wins: 20, losses: 87, playtime: 113, codeLanguage: 'javascript'} + {team: 'humans', rank: 96, userID: '54fad0665d8ec82806aad9bf', sessionID: '550d00129e22dc56056be0d7', name: 'vanessatan', wins: 19, losses: 89, playtime: 5013, codeLanguage: 'python'} + {team: 'humans', rank: 96, userID: '5487a14a4647d2b006cf7edd', sessionID: '551854a1e3ae3a3505f7c904', name: 'Endercore79', wins: 19, losses: 89, playtime: 769, codeLanguage: 'python'} + {team: 'humans', rank: 96, userID: '53416ac43a7f7a6358b7029a', sessionID: '55166a794d24212f05944596', name: 'Lylo', wins: 19, losses: 89, playtime: 359, codeLanguage: 'python'} + {team: 'humans', rank: 99, userID: '5470e99829e4c0710587df4c', sessionID: '5521b204abdb444805e883bb', name: 'Sthomas', wins: 18, losses: 89, playtime: 396, codeLanguage: 'javascript'} + {team: 'humans', rank: 99, userID: '54f240e481f9a6750530ed0c', sessionID: '5516db0153665d3005253b4d', name: 'Bosdet', wins: 18, losses: 89, playtime: 5448, codeLanguage: 'python'} + {team: 'humans', rank: 101, userID: '5495c837fcf9386d07d30866', sessionID: '550c73e1a98c9c7f05ab03ac', name: 'awesome3000', wins: 18, losses: 90, playtime: 2586, codeLanguage: 'python'} + {team: 'humans', rank: 102, userID: '54c3a0aa83b9575305363fbd', sessionID: '5515f79482ee611b05aeb454', name: 'ninja_star', wins: 17, losses: 90, playtime: 423, codeLanguage: 'javascript'} + {team: 'humans', rank: 103, userID: '54cbaa236947cf5405dab7e4', sessionID: '550cd526ca461f5705f3d1a0', name: 'hannie', wins: 16, losses: 90, playtime: 2400, codeLanguage: 'python'} + {team: 'humans', rank: 104, userID: '550cc15b0f7e6ece0684a2d4', sessionID: '550cc195fa4ee5e0062ed84d', name: 'The AI', wins: 15, losses: 92, playtime: 385, codeLanguage: 'clojure'} + {team: 'humans', rank: 105, userID: '546656d5f5522b9805dfd4ca', sessionID: '551f09a0921f633305d4d7d8', name: '1234526', wins: 15, losses: 93, playtime: 909, codeLanguage: 'javascript'} + {team: 'humans', rank: 106, userID: '5500418620b2567d0514b763', sessionID: '550d0037a605ba5a051205da', name: 'luxedlyani', wins: 14, losses: 94, playtime: 5487, codeLanguage: 'python'} + {team: 'humans', rank: 106, userID: '532795fcd2a31ed46c63770e', sessionID: '55205354be496e310568fe50', name: 'kevbot', wins: 14, losses: 94, playtime: 2118, codeLanguage: 'python'} + {team: 'humans', rank: 108, userID: '53de2603f9fd4335057a50ca', sessionID: '551632595ab8ae3105cad77a', name: 'Sunnyau', wins: 12, losses: 96, playtime: 9882, codeLanguage: 'python'} + {team: 'humans', rank: 109, userID: '550aa9c9c731583007335e14', sessionID: '55158850d792f354050c7eb5', name: 'Anonymous', wins: 11, losses: 97, playtime: 137, codeLanguage: 'python'} + {team: 'humans', rank: 110, userID: '551095ad3ab45d7d05d0290f', sessionID: '55209eeed06c123005054edf', name: 'The Dark Slayer 365', wins: 10, losses: 98, playtime: 695, codeLanguage: 'python'} + {team: 'humans', rank: 111, userID: '5519cb4ae3ae3a3505f828fe', sessionID: '551a570dd69ccd3305a34080', name: 'weiyun', wins: 9, losses: 99, playtime: 2574, codeLanguage: 'python'} + {team: 'humans', rank: 112, userID: '551285ff39ac155805f4aa0a', sessionID: '551e83bee311d80c07b35d46', name: 'M. Vincent', wins: 8, losses: 100, playtime: 6127, codeLanguage: 'javascript'} + {team: 'humans', rank: 113, userID: '54f62d7d50181a8505f97e1d', sessionID: '551f1f29526d5e3505807c00', name: 'Oscha Esservic', wins: 0, losses: 108, playtime: 386, codeLanguage: 'javascript'} + {team: 'humans', rank: 113, userID: '52cfa17ec0e2ea5d17001149', sessionID: '551716f258852c3405f49269', name: 'DarkLynk', wins: 0, losses: 108, playtime: 189, codeLanguage: 'python'} + {team: 'humans', rank: 113, userID: '54f1f2f89bc914830528271f', sessionID: '550cc75a9e22dc56056bdfe4', name: 'Reilord', wins: 0, losses: 108, playtime: 4752, codeLanguage: 'python'} + {team: 'humans', rank: 113, userID: '54aab1313feb6a3f05f20854', sessionID: '551c82a25cd6bae409530abb', name: 'Flamethowerland', wins: 0, losses: 108, playtime: 506, codeLanguage: 'python'} +] + +results['zero-sum'].ogres = [ + {team: 'ogres', rank: 1, userID: '532dbc73a622924444b68ed9', sessionID: '551599a499d51b55053606f2', name: 'Wizard Dude', wins: 162, losses: 1, playtime: 43223, codeLanguage: 'javascript'} + {team: 'ogres', rank: 2, userID: '54ffc9b8a043d19b0a457309', sessionID: '551e1a2ae3ae3a3505f96090', name: 'xxx987654321', wins: 157, losses: 5, playtime: 11649, codeLanguage: 'python'} + {team: 'ogres', rank: 3, userID: '531c8c3ccf439d790a23af04', sessionID: '550c2f5c1cd64bb7050fc8e5', name: 'nemoyatpeace', wins: 153, losses: 9, playtime: 4171, codeLanguage: 'python'} + {team: 'ogres', rank: 4, userID: '54ccc53ab4fb475505282187', sessionID: '55168b6353665d3005252860', name: 'ImDev', wins: 152, losses: 10, playtime: 4842, codeLanguage: 'python'} + {team: 'ogres', rank: 5, userID: '551995ec5cd6bae40951dec9', sessionID: '5519969e5cd6bae40951df59', name: 'wrdctr', wins: 151, losses: 11, playtime: 22070, codeLanguage: 'python'} + {team: 'ogres', rank: 5, userID: '54d9a5d2a9bd9f57050fe822', sessionID: '550c48b13045b78405c96775', name: 'Vlevo', wins: 151, losses: 11, playtime: 41246, codeLanguage: 'clojure'} + {team: 'ogres', rank: 7, userID: '548b8c556c32e64e1c437957', sessionID: '551625ac5ab8ae3105cad65f', name: 'Nisha Noire', wins: 150, losses: 12, playtime: 6977, codeLanguage: 'python'} + {team: 'ogres', rank: 7, userID: '551824a31f12482609b44fad', sessionID: '55185272d69ccd3305a2ba9d', name: 'Ovrmrrw', wins: 150, losses: 12, playtime: 4139, codeLanguage: 'python'} + {team: 'ogres', rank: 9, userID: '5503ac1a2c5fbf7b056a0eff', sessionID: '551766524c7305350525145f', name: 'David C Ellis', wins: 147, losses: 14, playtime: 5804, codeLanguage: 'javascript'} + {team: 'ogres', rank: 10, userID: '5452ed0623ef1e5d089d31e2', sessionID: '55164ac8de8bc0320597591e', name: 'Vivaldi', wins: 146, losses: 15, playtime: 20229, codeLanguage: 'python'} + {team: 'ogres', rank: 11, userID: '52debd1b5432dcf11c000164', sessionID: '55162fc358852c3405f468f6', name: 'Agathanar', wins: 144, losses: 18, playtime: 13849, codeLanguage: 'python'} + {team: 'ogres', rank: 12, userID: '55169fb1de8bc03205976007', sessionID: '5516bbd7aedec33205e8b3c4', name: 'XN137', wins: 140, losses: 22, playtime: 44494, codeLanguage: 'javascript'} + {team: 'ogres', rank: 13, userID: '55172d51502e8b37053130b2', sessionID: '551840461f12482609b45518', name: 'Sean653', wins: 140, losses: 23, playtime: 5501, codeLanguage: 'javascript'} + {team: 'ogres', rank: 14, userID: '5512ef72a4febf5905274f07', sessionID: '551851355afdf53205577ac4', name: 'Vincent Maths', wins: 137, losses: 25, playtime: 2812, codeLanguage: 'javascript'} + {team: 'ogres', rank: 15, userID: '526a4e91c0a4f3a21f000c50', sessionID: '5516728baedec33205e8a5b9', name: 'spicydog', wins: 133, losses: 28, playtime: 3790, codeLanguage: 'python'} + {team: 'ogres', rank: 16, userID: '5511931af72de65605f988b4', sessionID: '5515a3e3bf2bea55051d2225', name: 'Deipablo', wins: 130, losses: 32, playtime: 9331, codeLanguage: 'javascript'} + {team: 'ogres', rank: 17, userID: '5515b4e1fd67e4b4095aaa5f', sessionID: '5515c034d792f354050ca69e', name: 'i_eet_leefs', wins: 128, losses: 34, playtime: 158, codeLanguage: 'python'} + {team: 'ogres', rank: 18, userID: '54ab32c13feb6a3f05f55e94', sessionID: '5515debc64a969570abf9827', name: 'SaladTongs', wins: 125, losses: 37, playtime: 26496, codeLanguage: 'python'} + {team: 'ogres', rank: 19, userID: '54cb0fbd0623615605392f74', sessionID: '550cc86d9e22dc56056bdfec', name: 'Dragernix', wins: 124, losses: 38, playtime: 4365, codeLanguage: 'python'} + {team: 'ogres', rank: 19, userID: '54f609476b0774880597fdd4', sessionID: '55171a58b77a16330597f89e', name: 'Danylo Dubinin', wins: 124, losses: 38, playtime: 6951, codeLanguage: 'python'} + {team: 'ogres', rank: 21, userID: '549aed0eb530133e053a3acc', sessionID: '55159f149f2b9c5305f551a0', name: 'Spencer Tachick', wins: 123, losses: 39, playtime: 12292, codeLanguage: 'python'} + {team: 'ogres', rank: 21, userID: '5507e6c8e7d9907b054a61e9', sessionID: '55167b894d24212f05944728', name: 'NormannK', wins: 123, losses: 39, playtime: 9490, codeLanguage: 'python'} + {team: 'ogres', rank: 23, userID: '549b8ead1880d5b7065ff57c', sessionID: '55161e90aedec33205e89e2c', name: 'Dymarr', wins: 116, losses: 46, playtime: 281, codeLanguage: 'python'} + {team: 'ogres', rank: 24, userID: '53ca0f0aaa30cd860586b24f', sessionID: '5521bf1b32954a32050203ba', name: 'Edgar', wins: 115, losses: 47, playtime: 4999, codeLanguage: 'javascript'} + {team: 'ogres', rank: 25, userID: '54fdacd0106d15f105fc7d4c', sessionID: '550cfffbca461f5705f3d1ec', name: 'Midhat', wins: 114, losses: 48, playtime: 5557, codeLanguage: 'python'} + {team: 'ogres', rank: 26, userID: '54f5d94c9be7567905e95099', sessionID: '551d1124d69ccd3305a43329', name: 's_a_n_d', wins: 114, losses: 49, playtime: 3905, codeLanguage: 'javascript'} + {team: 'ogres', rank: 27, userID: '5515ff2482ee611b05aeb6c6', sessionID: '5515ff641bfec61c050245ab', name: 'Carl Maxwell', wins: 113, losses: 48, playtime: 10208, codeLanguage: 'python'} + {team: 'ogres', rank: 28, userID: '55066eefce02078605ce9126', sessionID: '550d29999e22dc56056be28a', name: 'Koyint', wins: 113, losses: 49, playtime: 524, codeLanguage: 'python'} + {team: 'ogres', rank: 29, userID: '5517d8b0e3ae3a3505f7b73d', sessionID: '5517de29079f2833053c8108', name: 'aknopf', wins: 113, losses: 50, playtime: 203, codeLanguage: 'python'} + {team: 'ogres', rank: 30, userID: '54ffbe6ee950f7a707ec9577', sessionID: '550cc7bc9e22dc56056bdfe9', name: 'chuayupeng', wins: 112, losses: 50, playtime: 4602, codeLanguage: 'python'} + {team: 'ogres', rank: 31, userID: '5506889b127bb387059def67', sessionID: '550d001435dc145905672238', name: 'En Hao', wins: 111, losses: 51, playtime: 5234, codeLanguage: 'python'} + {team: 'ogres', rank: 32, userID: '527bdd42dd3c4aea37001500', sessionID: '55159c75b8185a5205e183dc', name: 'Makaze', wins: 109, losses: 53, playtime: 2213, codeLanguage: 'coffeescript'} + {team: 'ogres', rank: 33, userID: '55181575e3ae3a3505f7bf46', sessionID: '55191d035afdf532055796d6', name: 'Satellite One', wins: 106, losses: 51, playtime: 16911, codeLanguage: 'python'} + {team: 'ogres', rank: 34, userID: '54b1773a75e3055205e5a449', sessionID: '550c3a98535111cc0539a3f9', name: 'Catsync', wins: 107, losses: 54, playtime: 381, codeLanguage: 'javascript'} + {team: 'ogres', rank: 35, userID: '53a0bc84610a6b3505561811', sessionID: '5516185f53665d3005251ac7', name: 'Fluzzarn', wins: 102, losses: 60, playtime: 1835, codeLanguage: 'javascript'} + {team: 'ogres', rank: 36, userID: '5516bf048bdcbad0074f9c29', sessionID: '5516d0a258852c3405f481da', name: 'Expote2', wins: 100, losses: 62, playtime: 4576, codeLanguage: 'python'} + {team: 'ogres', rank: 37, userID: '54ff5e1a00e4485a07e76bb9', sessionID: '5515ac67fd67e4b4095aa52f', name: 'Twonky', wins: 96, losses: 66, playtime: 6772, codeLanguage: 'python'} + {team: 'ogres', rank: 38, userID: '550003910f91fab20b3624e8', sessionID: '550d00439e22dc56056be0de', name: 'CyanLycan', wins: 95, losses: 66, playtime: 5482, codeLanguage: 'python'} + {team: 'ogres', rank: 39, userID: '54f2b39381f9a6750530f893', sessionID: '550ccb259e22dc56056be001', name: 'Flamanta', wins: 95, losses: 68, playtime: 4734, codeLanguage: 'python'} + {team: 'ogres', rank: 40, userID: '54f0775615cde87c05d6caf4', sessionID: '550cc7ef35dc1459056721be', name: 'Jericho Chua', wins: 93, losses: 69, playtime: 4409, codeLanguage: 'python'} + {team: 'ogres', rank: 41, userID: '539bfd0930a67c3b05d93f2a', sessionID: '55158a0bf43d375105fbe4ec', name: 'reezer', wins: 93, losses: 70, playtime: 5116, codeLanguage: 'javascript'} + {team: 'ogres', rank: 42, userID: '54d2c4024e4a08550556e01c', sessionID: '550cd3269e22dc56056be050', name: 'Alaete', wins: 92, losses: 69, playtime: 1804, codeLanguage: 'python'} + {team: 'ogres', rank: 43, userID: '53e2f1046c59f534050411f3', sessionID: '550d000e35dc145905672236', name: 'Live2Deliver', wins: 91, losses: 71, playtime: 5362, codeLanguage: 'python'} + {team: 'ogres', rank: 44, userID: '5517858d079f2833053c79a7', sessionID: '551786c7e3ae3a3505f7b0f6', name: 'Kevbot5000', wins: 88, losses: 72, playtime: 93126, codeLanguage: 'javascript'} + {team: 'ogres', rank: 45, userID: '54f3527fde0f173f14215f40', sessionID: '552210f33ffd333305f2e9b5', name: 'Anonymous', wins: 88, losses: 73, playtime: 346, codeLanguage: 'javascript'} + {team: 'ogres', rank: 46, userID: '55184fc7c13fa0320524a596', sessionID: '5518516ac13fa0320524a5f1', name: 'Mossan', wins: 85, losses: 77, playtime: 11842, codeLanguage: 'python'} + {team: 'ogres', rank: 47, userID: '539bb1d46e1d92310506ec6b', sessionID: '551ab214d69ccd3305a35b6c', name: 'Belackarad', wins: 84, losses: 78, playtime: 2921, codeLanguage: 'python'} + {team: 'ogres', rank: 48, userID: '534316e1d9a976607ace1f48', sessionID: '5518c7954c73053505253f23', name: 'Racksickle', wins: 84, losses: 79, playtime: 4118, codeLanguage: 'javascript'} + {team: 'ogres', rank: 49, userID: '5515b3d599d51b55053619e3', sessionID: '5517f05fe3ae3a3505f7b998', name: 'Crul', wins: 82, losses: 81, playtime: 99, codeLanguage: 'javascript'} + {team: 'ogres', rank: 50, userID: '548bc8bdcb1a6d754f530ef9', sessionID: '5518c2a0d69ccd3305a2cd93', name: 'MonkeykingZach1', wins: 80, losses: 82, playtime: 2904, codeLanguage: 'javascript'} + {team: 'ogres', rank: 51, userID: '54ea122b6a05ed9a0803f20b', sessionID: '55181b4de3ae3a3505f7c028', name: 'All3ck', wins: 77, losses: 84, playtime: 16269, codeLanguage: 'python'} + {team: 'ogres', rank: 52, userID: '5522bf863ffd333305f30ef5', sessionID: '5522c90cabdb444805e8c0b9', name: 'Fireball42', wins: 73, losses: 87, playtime: 3431, codeLanguage: 'javascript'} + {team: 'ogres', rank: 53, userID: '5514a8636b06ed1e09bc4c01', sessionID: '55204b8ffe6383360550157d', name: 'Hephaestus2', wins: 73, losses: 89, playtime: 1162, codeLanguage: 'python'} + {team: 'ogres', rank: 54, userID: '539189a2ff512d470582a570', sessionID: '550cc7f3a605ba5a0512050f', name: 'Lee Yang Peng', wins: 72, losses: 88, playtime: 5743, codeLanguage: 'python'} + {team: 'ogres', rank: 55, userID: '546e31004a0dd3de07f2b3f0', sessionID: '5515a3c3518f4e1409379f15', name: 'Blauelf', wins: 72, losses: 91, playtime: 119, codeLanguage: 'python'} + {team: 'ogres', rank: 56, userID: '55125f4ba4febf590526f3ea', sessionID: '551a8d32d69ccd3305a34b40', name: 'Yan4ik', wins: 71, losses: 91, playtime: 7018, codeLanguage: 'python'} + {team: 'ogres', rank: 57, userID: '54cba6498a09e554056f7e83', sessionID: '550cce4f9e22dc56056be01a', name: 'Skyhawk5', wins: 70, losses: 92, playtime: 1415, codeLanguage: 'python'} + {team: 'ogres', rank: 58, userID: '550126ad9201157c05677836', sessionID: '550d003ba605ba5a051205dc', name: 'ohichimaru', wins: 66, losses: 96, playtime: 5118, codeLanguage: 'python'} + {team: 'ogres', rank: 59, userID: '54ea04ede90cfb5005381cae', sessionID: '550d00bc9e22dc56056be0e6', name: 'fafazirah', wins: 65, losses: 98, playtime: 2494, codeLanguage: 'python'} + {team: 'ogres', rank: 60, userID: '54fd92b847b0892406d76ee6', sessionID: '550d015d9e22dc56056be0f2', name: 'Soon Wei', wins: 64, losses: 98, playtime: 5270, codeLanguage: 'python'} + {team: 'ogres', rank: 61, userID: '537d66783dcf67c40571fce9', sessionID: '550976d42e7f640f07bf7cbc', name: 'ProfBoesch', wins: 61, losses: 101, playtime: 2684, codeLanguage: 'python'} + {team: 'ogres', rank: 62, userID: '54ef1ac12fd7dd55052b4bf6', sessionID: '550cc78ea605ba5a0512050d', name: 'ZoppyOS', wins: 60, losses: 101, playtime: 5566, codeLanguage: 'python'} + {team: 'ogres', rank: 63, userID: '54f1e0f2d2969f8405ef6e93', sessionID: '550d03639e22dc56056be123', name: 'hongyue1995', wins: 57, losses: 104, playtime: 3402, codeLanguage: 'python'} + {team: 'ogres', rank: 64, userID: '53b54cda7e17883a0575767a', sessionID: '5515bde81c3bb18709e26cd3', name: 'JavaCaster1', wins: 55, losses: 106, playtime: 91, codeLanguage: 'javascript'} + {team: 'ogres', rank: 65, userID: '54e3389fab6f345305701d9d', sessionID: '550d003a35dc145905672239', name: 'Aswin A', wins: 54, losses: 108, playtime: 4830, codeLanguage: 'python'} + {team: 'ogres', rank: 66, userID: '53b8f7ab3a63df45051b31d6', sessionID: '551ff648b10646310516d109', name: 'Elizabeth20', wins: 46, losses: 117, playtime: 3329, codeLanguage: 'javascript'} + {team: 'ogres', rank: 67, userID: '54f82214cebfc7450c16bbe9', sessionID: '550cc7f49e22dc56056bdfeb', name: 'Chaoxide', wins: 45, losses: 118, playtime: 5296, codeLanguage: 'python'} + {team: 'ogres', rank: 68, userID: '54fa96ce9ae7221e0672306b', sessionID: '550cc7e29e22dc56056bdfea', name: 'Xyrilyn', wins: 41, losses: 121, playtime: 5516, codeLanguage: 'python'} + {team: 'ogres', rank: 69, userID: '54f420d8c43ea77705a27522', sessionID: '550cc78535dc1459056721bd', name: 'WangLu', wins: 40, losses: 122, playtime: 5460, codeLanguage: 'python'} + {team: 'ogres', rank: 70, userID: '54a976c1b9a10c3e059a5414', sessionID: '551c623f8328b9020decd431', name: 'germ13', wins: 35, losses: 127, playtime: 4753, codeLanguage: 'python'} + {team: 'ogres', rank: 71, userID: '52619a3e99987a8a0900009e', sessionID: '5516d34058852c3405f48284', name: 'iTruth', wins: 35, losses: 128, playtime: 5077, codeLanguage: 'javascript'} + {team: 'ogres', rank: 72, userID: '55197724e3ae3a3505f80446', sessionID: '551977265cd6bae40951cafb', name: 'Stowned', wins: 30, losses: 133, playtime: 1594, codeLanguage: 'python'} + {team: 'ogres', rank: 73, userID: '546e6be7da3c21f30848d235', sessionID: '551c5c0c1794dc1a112c536c', name: 'Jonas5', wins: 28, losses: 135, playtime: 663, codeLanguage: 'python'} + {team: 'ogres', rank: 73, userID: '55098a62a2012e800a354350', sessionID: '551dae53e3ae3a3505f94fb9', name: 'coding6', wins: 28, losses: 135, playtime: 66, codeLanguage: 'python'} + {team: 'ogres', rank: 73, userID: '54f160550ac4bd7c05458e7e', sessionID: '55166f02aedec33205e8a541', name: 'Dima2006', wins: 28, losses: 135, playtime: 61, codeLanguage: 'javascript'} + {team: 'ogres', rank: 76, userID: '54f8b3237a5c02820877960e', sessionID: '551aec19e3ae3a3505f87585', name: 'JakeWolt20', wins: 28, losses: 136, playtime: 34, codeLanguage: 'python'} + {team: 'ogres', rank: 77, userID: '54c6a197c597612313ca1520', sessionID: '5519a05c4c7305350525799e', name: 'maxx89', wins: 27, losses: 136, playtime: 408, codeLanguage: 'javascript'} + {team: 'ogres', rank: 78, userID: '550cc15b0f7e6ece0684a2d4', sessionID: '550cc20735f6fd2807b877ae', name: 'The AI', wins: 24, losses: 135, playtime: 128, codeLanguage: 'clojure'} + {team: 'ogres', rank: 79, userID: '54903e1e6f2e23450a4fd7ac', sessionID: '5519e6bc8328b9020debf33f', name: 'kev0', wins: 15, losses: 147, playtime: 3459, codeLanguage: 'python'} + {team: 'ogres', rank: 80, userID: '54fe734f834d927905307753', sessionID: '5515cefa1a93145605a66f9e', name: 'Anonymous', wins: 14, losses: 149, playtime: 1002, codeLanguage: 'python'} +] diff --git a/app/views/play/ladder/utils.coffee b/app/views/ladder/utils.coffee similarity index 100% rename from app/views/play/ladder/utils.coffee rename to app/views/ladder/utils.coffee diff --git a/app/views/modal/EmployerSignupModal.coffee b/app/views/modal/EmployerSignupModal.coffee index f7b8c547b..9f120d654 100644 --- a/app/views/modal/EmployerSignupModal.coffee +++ b/app/views/modal/EmployerSignupModal.coffee @@ -75,7 +75,7 @@ module.exports = class EmployerSignupModal extends ModalView handleAgreementSuccess: (result) -> window.tracker?.trackEvent 'Employer Agreed to Contract' - me.fetch() + me.fetch cache: false window.location.reload() handleAgreementFailure: (error) -> @@ -130,7 +130,7 @@ module.exports = class EmployerSignupModal extends ModalView @listenTo me, 'sync', => @render() IN.parse() - me.fetch() + me.fetch cache: false destroy: -> reloadWhenClosed = @reloadWhenClosed diff --git a/app/views/modal/ModelModal.coffee b/app/views/modal/ModelModal.coffee index 315d2db0c..20465586a 100644 --- a/app/views/modal/ModelModal.coffee +++ b/app/views/modal/ModelModal.coffee @@ -1,5 +1,6 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/modal/model' +template = require 'templates/modal/model-modal' +require 'vendor/treema' module.exports = class ModelModal extends ModalView id: 'model-modal' @@ -13,7 +14,7 @@ module.exports = class ModelModal extends ModalView @models = options.models for model in @models when not model.loaded @supermodel.loadModel model, 'source_document' - model.fetch() + model.fetch cache: false getRenderData: -> c = super() diff --git a/app/views/modal/RevertModal.coffee b/app/views/modal/RevertModal.coffee index d931e9dbf..f6f977c75 100644 --- a/app/views/modal/RevertModal.coffee +++ b/app/views/modal/RevertModal.coffee @@ -1,5 +1,5 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/modal/revert' +template = require 'templates/modal/revert-modal' CocoModel = require 'models/CocoModel' module.exports = class RevertModal extends ModalView diff --git a/app/views/modal/WizardSettingsModal.coffee b/app/views/modal/WizardSettingsModal.coffee deleted file mode 100644 index d2dc85ba4..000000000 --- a/app/views/modal/WizardSettingsModal.coffee +++ /dev/null @@ -1,60 +0,0 @@ -ModalView = require 'views/core/ModalView' -template = require 'templates/modal/wizard_settings' -WizardLank = require 'lib/surface/WizardLank' -ThangType = require 'models/ThangType' -{me} = require 'core/auth' -forms = require 'core/forms' -User = require 'models/User' - -module.exports = class WizardSettingsModal extends ModalView - id: 'wizard-settings-modal' - template: template - closesOnClickOutside: false - - events: - 'keyup #wizard-settings-name': -> @trigger 'nameChanged' - 'click #wizard-settings-done': 'onWizardSettingsDone' - - constructor: (options) -> - @onNameChange = _.debounce(@checkNameExists, 500) - @on 'nameChanged', @onNameChange - super options - - afterRender: -> - WizardSettingsView = require 'views/account/WizardSettingsView' - view = new WizardSettingsView() - @insertSubView view - super() - - checkNameExists: => - forms.clearFormAlerts(@$el) - name = $('#wizard-settings-name').val() - User.getUnconflictedName name, (newName) => - forms.clearFormAlerts(@$el) - if name isnt newName - forms.setErrorToProperty @$el, 'name', 'This name is already taken so you won\'t be able to keep it.', true - - onWizardSettingsDone: -> - me.set('name', $('#wizard-settings-name').val()) - forms.clearFormAlerts(@$el) - res = me.validate() - if res? - forms.applyErrorsToForm(@$el, res) - return - - res = me.patch() - return unless res - save = $('#save-button', @$el).text($.i18n.t('common.saving', defaultValue: 'Saving...')) - .addClass('btn-info').show().removeClass('btn-danger') - - res.error => - errors = JSON.parse(res.responseText) - console.warn 'Got errors saving user:', errors - return if @destroyed - forms.applyErrorsToForm(@$el, errors) - @disableModalInProgress(@$el) - - res.success (model, response, options) => - @hide() unless @destroyed - - @enableModalInProgress(@$el) diff --git a/app/views/play/CampaignView.coffee b/app/views/play/CampaignView.coffee new file mode 100644 index 000000000..f5d50e172 --- /dev/null +++ b/app/views/play/CampaignView.coffee @@ -0,0 +1,646 @@ +RootView = require 'views/core/RootView' +template = require 'templates/play/campaign-view' +LevelSession = require 'models/LevelSession' +EarnedAchievement = require 'models/EarnedAchievement' +CocoCollection = require 'collections/CocoCollection' +Campaign = require 'models/Campaign' +AudioPlayer = require 'lib/AudioPlayer' +LevelSetupManager = require 'lib/LevelSetupManager' +ThangType = require 'models/ThangType' +MusicPlayer = require 'lib/surface/MusicPlayer' +storage = require 'core/storage' +AuthModal = require 'views/core/AuthModal' +SubscribeModal = require 'views/core/SubscribeModal' +LeaderboardModal = require 'views/play/modal/LeaderboardModal' +Level = require 'models/Level' +utils = require 'core/utils' +require 'vendor/three' +ParticleMan = require 'core/ParticleMan' +ShareProgressModal = require 'views/play/modal/ShareProgressModal' +UserPollsRecord = require 'models/UserPollsRecord' +Poll = require 'models/Poll' +PollModal = require 'views/play/modal/PollModal' +storage = require 'core/storage' + +trackedHourOfCode = false + +class LevelSessionsCollection extends CocoCollection + url: '' + model: LevelSession + + constructor: (model) -> + super() + @url = "/db/user/#{me.id}/level.sessions?project=state.complete,levelID,state.difficulty" + +class CampaignsCollection extends CocoCollection + url: '/db/campaign' + model: Campaign + project: ['name', 'fullName', 'description', 'i18n'] + +module.exports = class CampaignView extends RootView + id: 'campaign-view' + template: template + + subscriptions: + 'subscribe-modal:subscribed': 'onSubscribed' + + events: + 'click .map-background': 'onClickMap' + 'click .level a': 'onClickLevel' + 'dblclick .level a': 'onDoubleClickLevel' + 'click .level-info-container .start-level': 'onClickStartLevel' + 'click .level-info-container .view-solutions': 'onClickViewSolutions' + 'click #volume-button': 'onToggleVolume' + 'click #back-button': 'onClickBack' + 'click #clear-storage-button': 'onClickClearStorage' + 'click .portal .campaign': 'onClickPortalCampaign' + 'mouseenter .portals': 'onMouseEnterPortals' + 'mouseleave .portals': 'onMouseLeavePortals' + 'mousemove .portals': 'onMouseMovePortals' + 'click .poll': 'showPoll' + + constructor: (options, @terrain) -> + super options + @editorMode = options?.editorMode + if @editorMode + @terrain ?= 'dungeon' + @levelStatusMap = {} + @levelPlayCountMap = {} + @levelDifficultyMap = {} + @sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', {cache: false}, 0).model + @listenToOnce @sessions, 'sync', @onSessionsLoaded + unless @terrain + @campaigns = @supermodel.loadCollection(new CampaignsCollection(), 'campaigns', null, 0).model + @listenToOnce @campaigns, 'sync', @onCampaignsLoaded + return + + @campaign = new Campaign({_id:@terrain}) + @campaign.saveBackups = @editorMode + @campaign = @supermodel.loadModel(@campaign, 'campaign').model + + # Temporary attempt to make sure all earned rewards are accounted for. Figure out a better solution... + @earnedAchievements = new CocoCollection([], {url: '/db/earned_achievement', model:EarnedAchievement, project: ['earnedRewards']}) + @listenToOnce @earnedAchievements, 'sync', -> + earned = me.get('earned') + for m in @earnedAchievements.models + continue unless loadedEarned = m.get('earnedRewards') + for group in ['heroes', 'levels', 'items'] + continue unless loadedEarned[group] + for reward in loadedEarned[group] + if reward not in earned[group] + console.warn 'Filling in a gap for reward', group, reward + earned[group].push(reward) + + @supermodel.loadCollection(@earnedAchievements, 'achievements', {cache: false}) + + @listenToOnce @campaign, 'sync', @getLevelPlayCounts + $(window).on 'resize', @onWindowResize + @probablyCachedMusic = storage.load("loaded-menu-music") + musicDelay = if @probablyCachedMusic then 1000 else 10000 + @playMusicTimeout = _.delay (=> @playMusic() unless @destroyed), musicDelay + @hadEverChosenHero = me.get('heroConfig')?.thangType + @listenTo me, 'change:purchased', -> @renderSelectors('#gems-count') + @listenTo me, 'change:spent', -> @renderSelectors('#gems-count') + @listenTo me, 'change:earned', -> @renderSelectors('#gems-count') + @listenTo me, 'change:heroConfig', -> @updateHero() + window.tracker?.trackEvent 'Loaded World Map', category: 'World Map', label: @terrain + + # If it's a new player who didn't appear to come from Hour of Code, we register her here without setting the hourOfCode property. + elapsed = (new Date() - new Date(me.get('dateCreated'))) + if not trackedHourOfCode and not me.get('hourOfCode') and elapsed < 5 * 60 * 1000 + $('body').append($('<img src="https://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">')) + trackedHourOfCode = true + + @requiresSubscription = not me.isPremium() + + destroy: -> + @setupManager?.destroy() + @$el.find('.ui-draggable').off().draggable 'destroy' + $(window).off 'resize', @onWindowResize + if ambientSound = @ambientSound + # Doesn't seem to work; stops immediately. + createjs.Tween.get(ambientSound).to({volume: 0.0}, 1500).call -> ambientSound.stop() + @musicPlayer?.destroy() + clearTimeout @playMusicTimeout + @particleMan?.destroy() + clearInterval @portalScrollInterval + super() + + getLevelPlayCounts: -> + return unless me.isAdmin() + return # TODO: get rid of all this? It's redundant with new campaign editor analytics, unless we want to show player counts on leaderboards buttons. + success = (levelPlayCounts) => + return if @destroyed + for level in levelPlayCounts + @levelPlayCountMap[level._id] = playtime: level.playtime, sessions: level.sessions + @render() if @fullyRendered + + levelSlugs = (level.slug for levelID, level of @campaign.get 'levels') + levelPlayCountsRequest = @supermodel.addRequestResource 'play_counts', { + url: '/db/level/-/play_counts' + data: {ids: levelSlugs} + method: 'POST' + success: success + }, 0 + levelPlayCountsRequest.load() + + onLoaded: -> + return if @fullyRendered + @fullyRendered = true + @render() + @preloadTopHeroes() unless me.get('heroConfig')?.thangType + @$el.find('#campaign-status').delay(4000).animate({top: "-=58"}, 1000) unless @terrain is 'dungeon' + if @terrain and me.get('anonymous') and me.get('lastLevel') is 'shadow-guard' and me.level() < 4 + @openModalView new AuthModal supermodel: @supermodel, showSignupRationale: true, mode: 'signup' + else if @terrain and me.get('name') and me.get('lastLevel') in ['forgetful-gemsmith', 'signs-and-portents'] and me.level() < 5 and not (me.get('ageRange') in ['18-24', '25-34', '35-44', '45-100']) and not storage.load('sent-parent-email') and not me.isPremium() + @openModalView new ShareProgressModal() + + setCampaign: (@campaign) -> + @render() + + onSubscribed: -> + @requiresSubscription = false + @render() + + getRenderData: (context={}) -> + context = super(context) + context.campaign = @campaign + context.levels = _.values($.extend true, {}, @campaign?.get('levels') ? {}) + if me.level() < 12 and @terrain is 'dungeon' and not @editorMode + reject = if me.getFourthLevelGroup() is 'signs-and-portents' then 'forgetful-gemsmith' else 'signs-and-portents' + context.levels = _.reject context.levels, slug: reject + @annotateLevel level for level in context.levels + count = @countLevels context.levels + context.levelsCompleted = count.completed + context.levelsTotal = count.total + + @determineNextLevel context.levels if @sessions?.loaded + # put lower levels in last, so in the world map they layer over one another properly. + context.levels = (_.sortBy context.levels, (l) -> l.position.y).reverse() + @campaign.renderedLevels = context.levels if @campaign + + context.levelStatusMap = @levelStatusMap + context.levelDifficultyMap = @levelDifficultyMap + context.levelPlayCountMap = @levelPlayCountMap + context.isIPadApp = application.isIPadApp + context.mapType = _.string.slugify @terrain + context.requiresSubscription = @requiresSubscription + context.editorMode = @editorMode + context.adjacentCampaigns = _.filter _.values(_.cloneDeep(@campaign?.get('adjacentCampaigns') or {})), (ac) => + return false if ac.showIfUnlocked and (ac.showIfUnlocked not in me.levels()) and not @editorMode + ac.name = utils.i18n ac, 'name' + styles = [] + styles.push "color: #{ac.color}" if ac.color + styles.push "transform: rotate(#{ac.rotation}deg)" if ac.rotation + ac.position ?= { x: 10, y: 10 } + styles.push "left: #{ac.position.x}%" + styles.push "top: #{ac.position.y}%" + ac.style = styles.join('; ') + return true + context.marked = marked + context.i18n = utils.i18n + + if @campaigns + context.campaigns = {} + for campaign in @campaigns.models when campaign.get('slug') isnt 'auditions' + context.campaigns[campaign.get('slug')] = campaign + if @sessions.loaded + levels = _.values($.extend true, {}, campaign.get('levels') ? {}) + count = @countLevels levels + campaign.levelsTotal = count.total + campaign.levelsCompleted = count.completed + if campaign.get('slug') is 'dungeon' + campaign.locked = false + else unless campaign.levelsTotal + campaign.locked = true + else + campaign.locked = true + for campaign in @campaigns.models + for acID, ac of campaign.get('adjacentCampaigns') ? {} + _.find(@campaigns.models, id: acID)?.locked = false if ac.showIfUnlocked in me.levels() + + context + + afterRender: -> + super() + @onWindowResize() + unless application.isIPadApp + _.defer => @$el?.find('.game-controls .btn:not(.poll)').addClass('has-tooltip').tooltip() # Have to defer or i18n doesn't take effect. + view = @ + @$el.find('.level, .campaign-switch').addClass('has-tooltip').tooltip().each -> + return unless me.isAdmin() and view.editorMode + $(@).draggable().on 'dragstop', -> + bg = $('.map-background') + x = ($(@).offset().left - bg.offset().left + $(@).outerWidth() / 2) / bg.width() + y = 1 - ($(@).offset().top - bg.offset().top + $(@).outerHeight() / 2) / bg.height() + e = { position: { x: (100 * x), y: (100 * y) }, levelOriginal: $(@).data('level-original'), campaignID: $(@).data('campaign-id') } + view.trigger 'level-moved', e if e.levelOriginal + view.trigger 'adjacent-campaign-moved', e if e.campaignID + @updateVolume() + @updateHero() + unless window.currentModal or not @fullyRendered + @highlightElement '.level.next', delay: 500, duration: 60000, rotation: 0, sides: ['top'] + if @editorMode + for level in @campaign?.renderedLevels ? [] + for nextLevelOriginal in level.nextLevels ? [] + if nextLevel = _.find(@campaign.renderedLevels, original: nextLevelOriginal) + @createLine level.position, nextLevel.position + @showLeaderboard @options.justBeatLevel?.get('slug') if @options.showLeaderboard# or true # Testing + @applyCampaignStyles() + @testParticles() + + afterInsert: -> + super() + return unless @getQueryVariable 'signup' + return if me.get('email') + @endHighlight() + authModal = new AuthModal supermodel: @supermodel + authModal.mode = 'signup' + @openModalView authModal + + annotateLevel: (level) -> + level.position ?= { x: 10, y: 10 } + level.locked = not me.ownsLevel level.original + level.locked = false if @levelStatusMap[level.slug] in ['started', 'complete'] + level.locked = false if @editorMode + level.locked = false if @campaign?.get('name') is 'Auditions' + level.locked = false if @campaign?.get('name') is 'Intro' + level.locked = false if me.isInGodMode() + level.disabled = true if level.adminOnly and @levelStatusMap[level.slug] not in ['started', 'complete'] + level.disabled = false if me.isInGodMode() + level.color = 'rgb(255, 80, 60)' + if level.requiresSubscription + level.color = 'rgb(80, 130, 200)' + if unlocksHero = _.find(level.rewards, 'hero')?.hero + level.unlocksHero = unlocksHero + if level.unlocksHero + level.purchasedHero = level.unlocksHero in (me.get('purchased')?.heroes or []) + level.hidden = level.locked + if level.concepts?.length + level.displayConcepts = level.concepts + maxConcepts = 6 + if level.displayConcepts.length > maxConcepts + level.displayConcepts = level.displayConcepts.slice -maxConcepts + level + + countLevels: (levels) -> + count = total: 0, completed: 0 + for level, levelIndex in levels + @annotateLevel level unless level.locked? # Annotate if we haven't already. + unless level.disabled + unlockedInSameCampaign = levelIndex < 5 # First few are always counted (probably unlocked in previous campaign) + for otherLevel in levels when not unlockedInSameCampaign and otherLevel isnt level + for reward in (otherLevel.rewards ? []) when reward.level + unlockedInSameCampaign ||= reward.level is level.original + ++count.total if unlockedInSameCampaign or not level.locked + ++count.completed if @levelStatusMap[level.slug] is 'complete' + count + + showLeaderboard: (levelSlug) -> + leaderboardModal = new LeaderboardModal supermodel: @supermodel, levelSlug: levelSlug + @openModalView leaderboardModal + + determineNextLevel: (levels) -> + foundNext = false + for level in levels + level.nextLevels = (reward.level for reward in level.rewards ? [] when reward.level) + unless foundNext + for nextLevelOriginal in level.nextLevels + nextLevel = _.find levels, original: nextLevelOriginal + dontPointTo = ['lost-viking','kithgard-mastery'] + if nextLevel and not nextLevel.locked and not nextLevel.disabled and @levelStatusMap[nextLevel.slug] isnt 'complete' and nextLevel.slug not in dontPointTo and not nextLevel.replayable and ( + me.isPremium() or + not nextLevel.requiresSubscription or + (nextLevel.slug is 'boom-and-bust' and not @levelStatusMap['defense-of-plainswood']) or + (nextLevel.slug is 'favorable-odds' and not @levelStatusMap['the-raised-sword']) + ) + nextLevel.next = true + foundNext = true + break + if not foundNext and levels[0] and not levels[0].locked and @levelStatusMap[levels[0].slug] isnt 'complete' + levels[0].next = true + + createLine: (o1, o2) -> + p1 = x: o1.x, y: 0.66 * o1.y + 0.5 + p2 = x: o2.x, y: 0.66 * o2.y + 0.5 + length = Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) + angle = Math.atan2(p1.y - p2.y, p2.x - p1.x) * 180 / Math.PI + transform = "rotate(#{angle}deg)" + line = $('<div>').appendTo('.map').addClass('next-level-line').css(transform: transform, width: length + '%', left: o1.x + '%', bottom: (o1.y + 0.5) + '%') + line.append($('<div class="line">')).append($('<div class="point">')) + + applyCampaignStyles: -> + return unless @campaign?.loaded + if (backgrounds = @campaign.get 'backgroundImage') and backgrounds.length + backgrounds = _.sortBy backgrounds, 'width' + backgrounds.reverse() + rules = [] + for background, i in backgrounds + rule = "#campaign-view .map-background { background-image: url(/file/#{background.image}); }" + rule = "@media screen and (max-width: #{background.width}px) { #{rule} }" if i + rules.push rule + utils.injectCSS rules.join('\n') + if backgroundColor = @campaign.get 'backgroundColor' + backgroundColorTransparent = @campaign.get 'backgroundColorTransparent' + @$el.css 'background-color', backgroundColor + for pos in ['top', 'right', 'bottom', 'left'] + @$el.find(".#{pos}-gradient").css 'background-image', "linear-gradient(to #{pos}, #{backgroundColorTransparent} 0%, #{backgroundColor} 100%)" + @playAmbientSound() + + testParticles: -> + return unless @campaign?.loaded and $.browser.chrome # Sometimes this breaks in non-Chrome browsers, according to A/B tests. + @particleMan ?= new ParticleMan() + @particleMan.removeEmitters() + @particleMan.attach @$el.find('.map') + for level in @campaign.renderedLevels ? {} + particleKey = ['level', @terrain] + particleKey.push level.type if level.type and not (level.type in ['hero', 'course']) + particleKey.push 'replayable' if level.replayable + particleKey.push 'premium' if level.requiresSubscription + particleKey.push 'gate' if level.slug in ['kithgard-gates', 'siege-of-stonehold', 'clash-of-clones', 'summits-gate'] + particleKey.push 'hero' if level.unlocksHero and not level.unlockedHero + #particleKey.push 'item' if level.slug is 'apocalypse' # TODO: generalize + continue if particleKey.length is 2 # Don't show basic levels + continue unless level.hidden or _.intersection(particleKey, ['item', 'hero-ladder', 'replayable']).length + @particleMan.addEmitter level.position.x / 100, level.position.y / 100, particleKey.join('-') + + onMouseEnterPortals: (e) -> + return unless @campaigns?.loaded and @sessions?.loaded + @portalScrollInterval = setInterval @onMouseMovePortals, 100 + @onMouseMovePortals e + + onMouseLeavePortals: (e) -> + return unless @portalScrollInterval + clearInterval @portalScrollInterval + @portalScrollInterval = null + + onMouseMovePortals: (e) => + return unless @portalScrollInterval + $portal = @$el.find('.portal') + $portals = @$el.find('.portals') + if e + @portalOffsetX = Math.round Math.max 0, e.clientX - $portal.offset().left + bodyWidth = $('body').innerWidth() + fraction = @portalOffsetX / bodyWidth + return if 0.2 < fraction < 0.8 + direction = if fraction < 0.5 then 1 else -1 + magnitude = 0.2 * bodyWidth * (if direction is -1 then fraction - 0.8 else 0.2 - fraction) / 0.2 + portalsWidth = 1902 # TODO: if we add campaigns or change margins, this will get out of date... + scrollTo = $portals.offset().left + direction * magnitude + scrollTo = Math.max bodyWidth - portalsWidth, scrollTo + scrollTo = Math.min 0, scrollTo + $portals.stop().animate {marginLeft: scrollTo}, 100, 'linear' + + onSessionsLoaded: (e) -> + return if @editorMode + for session in @sessions.models + @levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started' + @levelDifficultyMap[session.get('levelID')] = session.get('state').difficulty if session.get('state')?.difficulty + @render() + @loadUserPollsRecord() unless me.get 'anonymous' + + onCampaignsLoaded: (e) -> + @render() + + preloadLevel: (levelSlug) -> + levelURL = "/db/level/#{levelSlug}" + level = new Level().setURL levelURL + level = @supermodel.loadModel(level, 'level', null, 0).model + sessionURL = "/db/level/#{levelSlug}/session" + @preloadedSession = new LevelSession().setURL sessionURL + @listenToOnce @preloadedSession, 'sync', @onSessionPreloaded + @preloadedSession = @supermodel.loadModel(@preloadedSession, 'level_session', {cache: false}).model + @preloadedSession.levelSlug = levelSlug + + onSessionPreloaded: (session) -> + session.url = -> '/db/level.session/' + @id + levelElement = @$el.find('.level-info-container:visible') + return unless session.levelSlug is levelElement.data 'level-slug' + return unless difficulty = session.get('state')?.difficulty + badge = $("<span class='badge'>#{difficulty}</span>") + levelElement.find('.start-level .badge').remove() + levelElement.find('.start-level').append badge + + onClickMap: (e) -> + @$levelInfo?.hide() + if @sessions.models.length < 3 + # Restore the next level higlight for very new players who might otherwise get lost. + @highlightElement '.level.next', delay: 500, duration: 60000, rotation: 0, sides: ['top'] + + onClickLevel: (e) -> + e.preventDefault() + e.stopPropagation() + @$levelInfo?.hide() + levelElement = $(e.target).parents('.level') + levelSlug = levelElement.data('level-slug') + levelOriginal = levelElement.data('level-original') + if @editorMode + return @trigger 'level-clicked', levelOriginal + @$levelInfo = @$el.find(".level-info-container[data-level-slug=#{levelSlug}]").show() + @adjustLevelInfoPosition e + @endHighlight() + @preloadLevel levelSlug + + onDoubleClickLevel: (e) -> + return unless @editorMode + levelElement = $(e.target).parents('.level') + levelOriginal = levelElement.data('level-original') + @trigger 'level-double-clicked', levelOriginal + + onClickStartLevel: (e) -> + levelElement = $(e.target).parents('.level-info-container') + levelSlug = levelElement.data('level-slug') + levelOriginal = levelElement.data('level-original') + level = _.find _.values(@campaign.get('levels')), slug: levelSlug + + requiresSubscription = level.requiresSubscription or (me.get('chinaVersion') and not (level.slug in ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'forgetful-gemsmith', 'signs-and-portents', 'true-names'])) + canPlayAnyway = not @requiresSubscription or level.adventurer or @levelStatusMap[level.slug] + if requiresSubscription and not canPlayAnyway + @openModalView new SubscribeModal() + window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'map level clicked', level: levelSlug + else + @startLevel levelElement + window.tracker?.trackEvent 'Clicked Start Level', category: 'World Map', levelID: levelSlug + + startLevel: (levelElement) -> + @setupManager?.destroy() + levelSlug = levelElement.data 'level-slug' + session = @preloadedSession if @preloadedSession?.loaded and @preloadedSession.levelSlug is levelSlug + @setupManager = new LevelSetupManager supermodel: @supermodel, levelID: levelSlug, levelPath: levelElement.data('level-path'), levelName: levelElement.data('level-name'), hadEverChosenHero: @hadEverChosenHero, parent: @, session: session + @setupManager.open() + @$levelInfo?.hide() + + onClickViewSolutions: (e) -> + levelElement = $(e.target).parents('.level-info-container') + levelSlug = levelElement.data('level-slug') + level = _.find _.values(@campaign.get('levels')), slug: levelSlug + if level.type in ['hero-ladder', 'course-ladder'] + Backbone.Mediator.publish 'router:navigate', route: "/play/ladder/#{levelSlug}", viewClass: 'views/ladder/LadderView', viewArgs: [{supermodel: @supermodel}, levelSlug] + else + @showLeaderboard levelSlug + + adjustLevelInfoPosition: (e) -> + return unless @$levelInfo + @$map ?= @$el.find('.map') + mapOffset = @$map.offset() + mapX = e.pageX - mapOffset.left + mapY = e.pageY - mapOffset.top + margin = 20 + width = @$levelInfo.outerWidth() + @$levelInfo.css('left', Math.min(Math.max(margin, mapX - width / 2), @$map.width() - width - margin)) + height = @$levelInfo.outerHeight() + top = mapY - @$levelInfo.outerHeight() - 60 + if top < 100 + top = mapY + 60 + @$levelInfo.css('top', top) + + onWindowResize: (e) => + mapHeight = iPadHeight = 1536 + mapWidth = {dungeon: 2350, forest: 2500, auditions: 2500, desert: 2350, mountain: 2422, glacier: 2421}[@terrain] or 2350 + aspectRatio = mapWidth / mapHeight + pageWidth = @$el.width() + pageHeight = @$el.height() + widthRatio = pageWidth / mapWidth + heightRatio = pageHeight / mapHeight + # Make sure we can see the whole map, fading to background in one dimension. + if heightRatio <= widthRatio + # Left and right margin + resultingHeight = pageHeight + resultingWidth = resultingHeight * aspectRatio + else + # Top and bottom margin + resultingWidth = pageWidth + resultingHeight = resultingWidth / aspectRatio + resultingMarginX = (pageWidth - resultingWidth) / 2 + resultingMarginY = (pageHeight - resultingHeight) / 2 + @$el.find('.map').css(width: resultingWidth, height: resultingHeight, 'margin-left': resultingMarginX, 'margin-top': resultingMarginY) + @testParticles() if @particleMan + + playAmbientSound: -> + return if @ambientSound + return unless file = @campaign?.get('ambientSound')?[AudioPlayer.ext.substr 1] + src = "/file/#{file}" + unless AudioPlayer.getStatus(src)?.loaded + AudioPlayer.preloadSound src + Backbone.Mediator.subscribeOnce 'audio-player:loaded', @playAmbientSound, @ + return + @ambientSound = createjs.Sound.play src, loop: -1, volume: 0.1 + createjs.Tween.get(@ambientSound).to({volume: 0.5}, 1000) + + playMusic: -> + @musicPlayer = new MusicPlayer() + musicFile = '/music/music-menu' + Backbone.Mediator.publish 'music-player:play-music', play: true, file: musicFile + storage.save("loaded-menu-music", true) unless @probablyCachedMusic + + preloadTopHeroes: -> + for heroID in ['captain', 'knight'] + url = "/db/thang.type/#{ThangType.heroes[heroID]}/version" + continue if @supermodel.getModel url + fullHero = new ThangType() + fullHero.setURL url + @supermodel.loadModel fullHero, 'thang' + + updateVolume: (volume) -> + volume ?= me.get('volume') ? 1.0 + classes = ['vol-off', 'vol-down', 'vol-up'] + button = $('#volume-button', @$el) + button.toggleClass 'vol-off', volume <= 0.0 + button.toggleClass 'vol-down', 0.0 < volume < 1.0 + button.toggleClass 'vol-up', volume >= 1.0 + createjs.Sound.setVolume(if volume is 1 then 0.6 else volume) # Quieter for now until individual sound FX controls work again. + if volume isnt me.get 'volume' + me.set 'volume', volume + me.patch() + + onToggleVolume: (e) -> + button = $(e.target).closest('#volume-button') + classes = ['vol-off', 'vol-down', 'vol-up'] + volumes = [0, 0.4, 1.0] + for oldClass, i in classes + if button.hasClass oldClass + newI = (i + 1) % classes.length + break + else if i is classes.length - 1 # no oldClass + newI = 2 + @updateVolume volumes[newI] + + onClickBack: (e) -> + Backbone.Mediator.publish 'router:navigate', + route: "/play" + viewClass: CampaignView + viewArgs: [{supermodel: @supermodel}] + + onClickClearStorage: (e) -> + localStorage.clear() + noty { + text: 'Local storage cleared. Reload to view the original campaign.' + layout: 'topCenter' + timeout: 5000 + type: 'information' + } + + updateHero: -> + return unless hero = me.get('heroConfig')?.thangType + for slug, original of ThangType.heroes when original is hero + @$el.find('.player-hero-icon').removeClass().addClass('player-hero-icon ' + slug) + return + console.error "CampaignView hero update couldn't find hero slug for original:", hero + + onClickPortalCampaign: (e) -> + campaign = $(e.target).closest('.campaign') + return if campaign.is('.locked') or campaign.is('.silhouette') + campaignSlug = campaign.data('campaign-slug') + Backbone.Mediator.publish 'router:navigate', + route: "/play/#{campaignSlug}" + viewClass: CampaignView + viewArgs: [{supermodel: @supermodel}, campaignSlug] + + loadUserPollsRecord: -> + url = "/db/user.polls.record/-/user/#{me.id}" + @userPollsRecord = new UserPollsRecord().setURL url + onRecordSync = -> + return if @destroyed + @userPollsRecord.url = -> '/db/user.polls.record/' + @id + lastVoted = new Date(@userPollsRecord.get('changed') or 0) + interval = new Date() - lastVoted + if interval > 22 * 60 * 60 * 1000 # Wait almost a day before showing the next poll + @loadPoll() + else + console.log 'Poll will be ready in', (22 * 60 * 60 * 1000 - interval) / (60 * 60 * 1000), 'hours.' + @listenToOnce @userPollsRecord, 'sync', onRecordSync + @userPollsRecord = @supermodel.loadModel(@userPollsRecord, 'user_polls_record', null, 0).model + onRecordSync.call @ if @userPollsRecord.loaded + + loadPoll: -> + url = "/db/poll/#{@userPollsRecord.id}/next" + @poll = new Poll().setURL url + onPollSync = -> + return if @destroyed + @poll.url = -> '/db/poll/' + @id + _.delay (=> @activatePoll?()), 1000 + onPollError = (poll, response, request) -> + if response.status is 404 + console.log 'There are no more polls left.' + else + console.error "Couldn't load poll:", response.status, response.statusText + delete @poll + @listenToOnce @poll, 'sync', onPollSync + @listenToOnce @poll, 'error', onPollError + @poll = @supermodel.loadModel(@poll, 'poll', null, 0).model + onPollSync.call @ if @poll.loaded + + activatePoll: -> + pollTitle = utils.i18n @poll.attributes, 'name' + $pollButton = @$el.find('button.poll').removeClass('hidden').addClass('highlighted').attr(title: pollTitle).addClass('has-tooltip').tooltip title: pollTitle + if me.get('lastLevel') is 'shadow-guard' + @showPoll() + else + $pollButton.tooltip 'show' + + showPoll: -> + pollModal = new PollModal supermodel: @supermodel, poll: @poll, userPollsRecord: @userPollsRecord + @openModalView pollModal + $pollButton = @$el.find 'button.poll' + pollModal.on 'vote-updated', -> + $pollButton.removeClass('highlighted').tooltip 'hide' diff --git a/app/views/play/MainPlayView.coffee b/app/views/play/MainPlayView.coffee deleted file mode 100644 index 65c8aa32d..000000000 --- a/app/views/play/MainPlayView.coffee +++ /dev/null @@ -1,337 +0,0 @@ -RootView = require 'views/core/RootView' -template = require 'templates/main-play-view' -LevelSession = require 'models/LevelSession' -CocoCollection = require 'collections/CocoCollection' - -class LevelSessionsCollection extends CocoCollection - url: '' - model: LevelSession - - constructor: (model) -> - super() - @url = "/db/user/#{me.id}/level.sessions?project=state.complete,levelID" - -module.exports = class MainPlayView extends RootView - id: 'play-view' - template: template - - constructor: (options) -> - super options - @levelStatusMap = {} - @levelPlayCountMap = {} - @sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model - @listenToOnce @sessions, 'sync', @onSessionsLoaded - @getLevelPlayCounts() - - onSessionsLoaded: (e) -> - for session in @sessions.models - @levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started' - @render() - - getLevelPlayCounts: -> - success = (levelPlayCounts) => - return if @destroyed - for level in levelPlayCounts - @levelPlayCountMap[level._id] = playtime: level.playtime, sessions: level.sessions - @render() if @supermodel.finished() - - levelIDs = [] - for campaign in campaigns - for level in campaign.levels - levelIDs.push level.id - levelPlayCountsRequest = @supermodel.addRequestResource 'play_counts', { - url: '/db/level/-/play_counts' - data: {ids: levelIDs} - method: 'POST' - success: success - }, 0 - levelPlayCountsRequest.load() - - getRenderData: (context={}) -> - context = super(context) - context.campaigns = campaigns - context.levelStatusMap = @levelStatusMap - context.levelPlayCountMap = @levelPlayCountMap - context - - -tutorials = [ - { - name: 'Rescue Mission' - difficulty: 1 - id: 'rescue-mission' - image: '/file/db/level/52740644904ac0411700067c/rescue_mission_icon.png' - description: 'Tharin has been captured!' - } - { - name: 'Grab the Mushroom' - difficulty: 1 - id: 'grab-the-mushroom' - image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png' - description: 'Grab a powerup and smash a big ogre.' - } - { - name: 'Drink Me' - difficulty: 1 - id: 'drink-me' - image: '/file/db/level/525dc5589a0765e496000006/drink_me_icon.png' - description: 'Drink up and slay two munchkins.' - } - { - name: 'Taunt the Guards' - difficulty: 1 - id: 'taunt-the-guards' - image: '/file/db/level/5276c9bdcf83207a2801ff8f/taunt_icon.png' - description: 'Tharin, if clever, can escape with Phoebe.' - } - { - name: 'It\'s a Trap' - difficulty: 1 - id: 'its-a-trap' - image: '/file/db/level/528aea2d7f37fc4e0700016b/its_a_trap_icon.png' - description: 'Organize a dungeon ambush with archers.' - } - { - name: 'Break the Prison' - difficulty: 1 - id: 'break-the-prison' - image: '/file/db/level/5275272c69abdcb12401216e/break_the_prison_icon.png' - description: 'More comrades are imprisoned!' - } - { - name: 'Taunt' - difficulty: 1 - id: 'taunt' - image: '/file/db/level/525f150306e1ab0962000018/taunt_icon.png' - description: 'Taunt the ogre to claim victory.' - } - { - name: 'Cowardly Taunt' - difficulty: 1 - id: 'cowardly-taunt' - image: '/file/db/level/525abfd9b12777d78e000009/cowardly_taunt_icon.png' - description: 'Lure infuriated ogres to their doom.' - } - { - name: 'Commanding Followers' - difficulty: 1 - id: 'commanding-followers' - image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png' - description: 'Lead allied soldiers into battle.' - } - { - name: 'Mobile Artillery' - difficulty: 1 - id: 'mobile-artillery' - image: '/file/db/level/525085419851b83f4b000001/mobile_artillery_icon.png' - description: 'Blow ogres up!' - } -] - -experienced = [ - { - name: 'Hunter Triplets' - difficulty: 2 - id: 'hunter-triplets' - image: '/file/db/level/526711d9add4f8965f000002/hunter_triplets_icon.png' - description: 'Three soldiers go ogre hunting.' - } - { - name: 'Emphasis on Aim' - difficulty: 2 - id: 'emphasis-on-aim' - image: '/file/db/level/525f384d96cd77000000000f/munchkin_masher_icon.png' - description: 'Choose your targets carefully.' - } - { - name: 'Zone of Danger' - difficulty: 3 - id: 'zone-of-danger' - image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png' - description: 'Target the ogres swarming into arrow range.' - } - { - name: 'Molotov Medic' - difficulty: 2 - id: 'molotov-medic' - image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png' - description: 'Tharin must play support in this dungeon battle.' - } - { - name: 'Gridmancer' - difficulty: 5 - id: 'gridmancer' - image: '/file/db/level/52ae2460ef42c52f13000008/gridmancer_icon.png' - description: 'Super algorithm challenge level!' - } -] - -arenas = [ - { - name: 'Criss-Cross' - difficulty: 5 - id: 'criss-cross' - image: '/file/db/level/528aea2d7f37fc4e0700016b/its_a_trap_icon.png' - description: 'Participate in a bidding war with opponents to reach the other side!' - levelPath: 'ladder' - } - { - name: 'Greed' - difficulty: 4 - id: 'greed' - image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png' - description: 'Liked Dungeon Arena and Gold Rush? Put them together in this economic arena!' - levelPath: 'ladder' - } - { - name: 'Dungeon Arena' - difficulty: 3 - id: 'dungeon-arena' - image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png' - description: 'Play head-to-head against fellow Wizards in a dungeon melee!' - levelPath: 'ladder' - } - { - name: 'Gold Rush' - difficulty: 3 - id: 'gold-rush' - image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png' - description: 'Prove you are better at collecting gold than your opponent!' - levelPath: 'ladder' - } - { - name: 'Brawlwood' - difficulty: 4 - id: 'brawlwood' - image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png' - description: 'Combat the armies of other Wizards in a strategic forest arena! (Fast computer required.)' - levelPath: 'ladder' - } - { - name: 'Sky Span (Testing)' - difficulty: 3 - id: 'sky-span' - image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png' - description: 'Preview version of an upgraded Dungeon Arena. Help us with hero balance before release!' - levelPath: 'ladder' - } -] - -classicAlgorithms = [ - { - name: 'Bubble Sort Bootcamp Battle' - difficulty: 3 - id: 'bubble-sort-bootcamp-battle' - image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png' - description: 'Write a bubble sort to organize your soldiers. - by Alexandru Caciulescu' - } - { - name: 'Ogres of Hanoi' - difficulty: 3 - id: 'ogres-of-hanoi' - image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png' - description: 'Transfer a stack of ogres while preserving their honor. - by Alexandru Caciulescu' - } - { - name: 'Danger! Minefield' - difficulty: 3 - id: 'danger-minefield' - image: '/file/db/level/526bda3fe79aefde2a003e36/mobile_artillery_icon.png' - description: 'Learn how to find prime numbers while defusing mines! - by Alexandru Caciulescu' - } - { - name: 'K-means++ Cluster Wars' - difficulty: 4 - id: 'k-means-cluster-wars' - image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png' - description: 'Learn cluster analysis while leading armies into battle! - by Alexandru Caciulescu' - } - { - name: 'Quicksort the Spiral' - difficulty: 3 - id: 'quicksort-the-spiral' - image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png' - description: 'Learn Quicksort while sorting a spiral of ogres! - by Alexandru Caciulescu' - } - { - name: 'Minimax Tic-Tac-Toe' - difficulty: 4 - id: 'minimax-tic-tac-toe' - image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png' - description: 'Learn how to make a game AI with the Minimax algorithm. - by Alexandru Caciulescu' - } -] - -playerCreated = [ - { - name: 'Extra Extrapolation' - difficulty: 2 - id: 'extra-extrapolation' - image: '/file/db/level/526bda3fe79aefde2a003e36/mobile_artillery_icon.png' - description: 'Predict your target\'s position for deadly aim. - by Sootn' - } - { - name: 'The Right Route' - difficulty: 1 - id: 'the-right-route' - image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png' - description: 'Strike at the weak point in an array of enemies. - by Aftermath' - } - { - name: 'Sword Loop' - difficulty: 2 - id: 'sword-loop' - image: '/file/db/level/525dc5589a0765e496000006/drink_me_icon.png' - description: 'Kill the ogres and save the peasants with for-loops. - by Prabh Simran Singh Baweja' - } - { - name: 'Coin Mania' - difficulty: 2 - id: 'coin-mania' - image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png' - description: 'Learn while-loops to grab coins and potions. - by Prabh Simran Singh Baweja' - } - { - name: 'Find the Spy' - difficulty: 2 - id: 'find-the-spy' - image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png' - description: 'Identify the spies hidden among your soldiers - by Nathan Gossett' - } - { - name: 'Harvest Time' - difficulty: 2 - id: 'harvest-time' - image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png' - description: 'Collect a hundred mushrooms in just five lines of code - by Nathan Gossett' - } - { - name: 'Guide Everyone Home' - difficulty: 2 - id: 'guide-everyone-home' - image: '/file/db/level/52740644904ac0411700067c/rescue_mission_icon.png' - description: 'Fetch the wizards teleporting into the area - by Nathan Gossett' - } - { - name: "Let's go Fly a Kite" - difficulty: 3 - id: 'lets-go-fly-a-kite' - image: '/file/db/level/526711d9add4f8965f000002/hunter_triplets_icon.png' - description: 'There is a horde of ogres marching on your village. Stay out of reach and use your bow to take them out! - by Danny Whittaker' - } - { - name: "IFC - Videira" - difficulty: 3 - id: 'ifc-videira' - image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png' - description: 'A level inspired by IFC Videira. - by Leonardo Meneguzzi.' - } -] - -campaigns = [ - {id: 'old_beginner', name: 'Old Beginner Campaign', description: '... in which you learn the wizardry of programming.', levels: tutorials} - {id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: arenas} - {id: 'dev', name: 'Random Harder Levels', description: '... in which you learn the interface while doing something a little harder.', levels: experienced} - {id: 'classic_algorithms' ,name: 'Classic Algorithms', description: '... in which you learn the most popular algorithms in Computer Science.', levels: classicAlgorithms} - {id: 'player_created', name: 'Player-Created', description: '... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>.', levels: playerCreated} -] diff --git a/app/views/play/SpectateView.coffee b/app/views/play/SpectateView.coffee index 0c0f27025..edf6796d5 100644 --- a/app/views/play/SpectateView.coffee +++ b/app/views/play/SpectateView.coffee @@ -119,19 +119,6 @@ module.exports = class SpectateLevelView extends RootView @register() @controlBar.setBus(@bus) @surface.showLevel() - if not (@level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop']) - if me.id isnt @session.get 'creator' - @surface.createOpponentWizard - id: @session.get('creator') - name: @session.get('creatorName') - team: @session.get('team') - levelSlug: @level.get('slug') - - @surface.createOpponentWizard - id: @otherSession.get('creator') - name: @otherSession.get('creatorName') - team: @otherSession.get('team') - levelSlug: @level.get('slug') grabLevelLoaderData: -> @session = @levelLoader.session @@ -187,13 +174,13 @@ module.exports = class SpectateLevelView extends RootView ctx.fillText("Loaded #{@modelsLoaded} thingies",50,50) insertSubviews: -> - @insertSubView @tome = new TomeView levelID: @levelID, session: @session, thangs: @world.thangs, supermodel: @supermodel, spectateView: true, spectateOpponentCodeLanguage: @otherSession?.get('submittedCodeLanguage'), level: @level - @insertSubView new PlaybackView {} + @insertSubView @tome = new TomeView levelID: @levelID, session: @session, otherSession: @otherSession, thangs: @world.thangs, supermodel: @supermodel, spectateView: true, spectateOpponentCodeLanguage: @otherSession?.get('submittedCodeLanguage'), level: @level + @insertSubView new PlaybackView session: @session, level: @level @insertSubView new GoldView {} - @insertSubView new HUDView {} + @insertSubView new HUDView {level: @level} worldName = utils.i18n @level.attributes, 'name' - @controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel, playableTeams: @world.playableTeams, spectateGame: true} + @controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel, spectateGame: true} # callbacks @@ -207,7 +194,7 @@ module.exports = class SpectateLevelView extends RootView initSurface: -> webGLSurface = $('canvas#webgl-surface', @$el) normalSurface = $('canvas#normal-surface', @$el) - @surface = new Surface(@world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, spectateGame: true, wizards: @level.get('type', true) isnt 'hero') + @surface = new Surface @world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, spectateGame: true, playerNames: @findPlayerNames() worldBounds = @world.getBounds() bounds = [{x:worldBounds.left, y:worldBounds.top}, {x:worldBounds.right, y:worldBounds.bottom}] @surface.camera.setBounds(bounds) @@ -215,6 +202,12 @@ module.exports = class SpectateLevelView extends RootView @surface.camera.zoomTo({x: (worldBounds.right - worldBounds.left) / 2, y: (worldBounds.top - worldBounds.bottom) / 2}, 0.1, 0) _.delay zoom, 4000 # call it later for some reason (TODO: figure this out) + findPlayerNames: -> + playerNames = {} + for session in [@session, @otherSession] when session?.get('team') + playerNames[session.get('team')] = session.get('creatorName') or 'Anoner' + playerNames + initGoalManager: -> @goalManager = new GoalManager(@world, @level.get('goals')) @god.setGoalManager @goalManager @@ -294,6 +287,7 @@ module.exports = class SpectateLevelView extends RootView $.ajax url: randomSessionPairURL type: 'GET' + cache: false complete: (jqxhr, textStatus) -> if textStatus isnt 'success' cb('error', jqxhr.statusText) diff --git a/app/views/play/WorldMapView.coffee b/app/views/play/WorldMapView.coffee deleted file mode 100644 index 789a1159b..000000000 --- a/app/views/play/WorldMapView.coffee +++ /dev/null @@ -1,896 +0,0 @@ -RootView = require 'views/core/RootView' -template = require 'templates/play/world-map-view' -LevelSession = require 'models/LevelSession' -EarnedAchievement = require 'models/EarnedAchievement' -CocoCollection = require 'collections/CocoCollection' -AudioPlayer = require 'lib/AudioPlayer' -LevelSetupManager = require 'lib/LevelSetupManager' -ThangType = require 'models/ThangType' -MusicPlayer = require 'lib/surface/MusicPlayer' -storage = require 'core/storage' -AuthModal = require 'views/core/AuthModal' - -trackedHourOfCode = false - -class LevelSessionsCollection extends CocoCollection - url: '' - model: LevelSession - - constructor: (model) -> - super() - @url = "/db/user/#{me.id}/level.sessions?project=state.complete,levelID" - -module.exports = class WorldMapView extends RootView - id: 'world-map-view' - template: template - - events: - 'click .map-background': 'onClickMap' - 'click .level a': 'onClickLevel' - 'click .level-info-container .start-level': 'onClickStartLevel' - 'mouseenter .level a': 'onMouseEnterLevel' - 'mouseleave .level a': 'onMouseLeaveLevel' - 'mousemove .map': 'onMouseMoveMap' - 'click #volume-button': 'onToggleVolume' - - constructor: (options, @terrain) -> - if options and application.isIPAdApp # TODO: later only clear the SuperModel if it has received a memory warning (not in app store yet) - options.supermodel = null - @terrain ?= 'dungeon' # or 'forest' - super options - @nextLevel = @getQueryVariable 'next' - @levelStatusMap = {} - @levelPlayCountMap = {} - @sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model - - # Temporary attempt to make sure all earned rewards are accounted for. Figure out a better solution... - @earnedAchievements = new CocoCollection([], {url: '/db/earned_achievement', model:EarnedAchievement, project: ['earnedRewards']}) - @listenToOnce @earnedAchievements, 'sync', -> - earned = me.get('earned') - addedSomething = false - for m in @earnedAchievements.models - continue unless loadedEarned = m.get('earnedRewards') - for group in ['heroes', 'levels', 'items'] - continue unless loadedEarned[group] - for reward in loadedEarned[group] - if reward not in earned[group] - console.warn 'Filling in a gap for reward', group, reward - earned[group].push(reward) - addedSomething = true - @supermodel.loadCollection(@earnedAchievements, 'achievements') - - @listenToOnce @sessions, 'sync', @onSessionsLoaded - @getLevelPlayCounts() - $(window).on 'resize', @onWindowResize - @playAmbientSound() - @probablyCachedMusic = storage.load("loaded-menu-music-#{@terrain}") - musicDelay = if @probablyCachedMusic then 1000 else 10000 - @playMusicTimeout = _.delay (=> @playMusic() unless @destroyed), musicDelay - @preloadTopHeroes() - @hadEverChosenHero = me.get('heroConfig')?.thangType - @listenTo me, 'change:purchased', -> @renderSelectors('#gems-count') - @listenTo me, 'change:spent', -> @renderSelectors('#gems-count') - window.tracker?.trackEvent 'Loaded World Map', category: 'World Map', ['Google Analytics'] - - # If it's a new player who didn't appear to come from Hour of Code, we register her here without setting the hourOfCode property. - elapsed = (new Date() - new Date(me.get('dateCreated'))) - if not trackedHourOfCode and not me.get('hourOfCode') and elapsed < 5 * 60 * 1000 - $('body').append($('<img src="http://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">')) - trackedHourOfCode = true - - destroy: -> - @setupManager?.destroy() - $(window).off 'resize', @onWindowResize - if ambientSound = @ambientSound - # Doesn't seem to work; stops immediately. - createjs.Tween.get(ambientSound).to({volume: 0.0}, 1500).call -> ambientSound.stop() - @musicPlayer?.destroy() - clearTimeout @playMusicTimeout - super() - - getLevelPlayCounts: -> - return unless me.isAdmin() - success = (levelPlayCounts) => - return if @destroyed - for level in levelPlayCounts - @levelPlayCountMap[level._id] = playtime: level.playtime, sessions: level.sessions - @render() if @supermodel.finished() - - levelIDs = [] - for campaign in campaigns - for level in campaign.levels - levelIDs.push level.id - levelPlayCountsRequest = @supermodel.addRequestResource 'play_counts', { - url: '/db/level/-/play_counts' - data: {ids: levelIDs} - method: 'POST' - success: success - }, 0 - levelPlayCountsRequest.load() - - getRenderData: (context={}) -> - context = super(context) - context.campaign = _.find campaigns, { id: @terrain } - for level, index in context.campaign.levels - level.x ?= 10 + 80 * Math.random() - level.y ?= 10 + 80 * Math.random() - level.locked = index > 0 and not me.ownsLevel level.original - window.levelUnlocksNotWorking = true if level.locked and level.id is @nextLevel # Temporary - level.locked = false if window.levelUnlocksNotWorking # Temporary; also possible in HeroVictoryModal - level.locked = false if @levelStatusMap[level.id] in ['started', 'complete'] - level.locked = false if me.get('slug') is 'nick' - level.disabled = false if @levelStatusMap[level.id] in ['started', 'complete'] - level.color = 'rgb(255, 80, 60)' - if level.practice - level.color = 'rgb(80, 130, 200)' unless me.getBranchingGroup() is 'all-practice' - level.hidden = true if me.getBranchingGroup() is 'no-practice' - context.levelStatusMap = @levelStatusMap - context.levelPlayCountMap = @levelPlayCountMap - context.isIPadApp = application.isIPadApp - context.mapType = _.string.slugify @terrain - context.nextLevel = @nextLevel - context.forestIsAvailable = @startedForestLevel or '541b67f71ccc8eaae19f3c62' in (me.get('earned')?.levels or []) - context - - afterRender: -> - super() - @onWindowResize() - unless application.isIPadApp - _.defer => @$el?.find('.game-controls .btn').tooltip() # Have to defer or i18n doesn't take effect. - @$el.find('.level').tooltip() - @$el.addClass _.string.slugify @terrain - @updateVolume() - unless window.currentModal or not @supermodel.finished() - @highlightElement '.level.next', delay: 500, duration: 60000, rotation: 0, sides: ['top'] - if levelID = @$el.find('.level.next').data('level-id') - @$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show() - pos = @$el.find('.level.next').offset() - @adjustLevelInfoPosition pageX: pos.left, pageY: pos.top - @manuallyPositionedLevelInfoID = levelID - - afterInsert: -> - super() - return unless @getQueryVariable 'signup' - return if me.get('email') - @endHighlight() - authModal = new AuthModal supermodel: @supermodel - authModal.mode = 'signup' - @openModalView authModal - - onSessionsLoaded: (e) -> - forestLevels = (f.id for f in forest) - for session in @sessions.models - @levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started' - @startedForestLevel = true if session.get('levelID') in forestLevels - if @nextLevel and @levelStatusMap[@nextLevel] is 'complete' - @nextLevel = null - @render() - - onClickMap: (e) -> - @$levelInfo?.hide() - # Easy-ish way of figuring out coordinates for placing level dots. - x = e.offsetX / @$el.find('.map-background').width() - y = (1 - e.offsetY / @$el.find('.map-background').height()) - console.log " x: #{(100 * x).toFixed(2)}\n y: #{(100 * y).toFixed(2)}\n" - - onClickLevel: (e) -> - e.preventDefault() - e.stopPropagation() - @$levelInfo?.hide() - if application.isIPadApp - levelID = $(e.target).parents('.level').data('level-id') - @$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show() - @adjustLevelInfoPosition e - @endHighlight() - else - if $(e.target).attr('disabled') - Backbone.Mediator.publish 'router:navigate', route: '/contribute/adventurer' - return - else if $(e.target).parent().hasClass 'locked' - return - else - levelElement = $(e.target).parents('.level') - levelID = levelElement.data('level-id') - @startLevel levelElement - window.tracker?.trackEvent 'Clicked Level', category: 'World Map', levelID: levelID, ['Google Analytics'] - - onClickStartLevel: (e) -> - levelElement = $(e.target).parents('.level-info-container') - @startLevel levelElement - window.tracker?.trackEvent 'Clicked Start Level', category: 'World Map', levelID: levelElement.data('level-id'), ['Google Analytics'] - - startLevel: (levelElement) -> - @setupManager?.destroy() - @setupManager = new LevelSetupManager supermodel: @supermodel, levelID: levelElement.data('level-id'), levelPath: levelElement.data('level-path'), levelName: levelElement.data('level-name'), hadEverChosenHero: @hadEverChosenHero, parent: @ - @setupManager.open() - @$levelInfo?.hide() - - onMouseEnterLevel: (e) -> - return if application.isIPadApp - levelID = $(e.target).parents('.level').data('level-id') - return if @manuallyPositionedLevelInfoID and levelID isnt @manuallyPositionedLevelInfoID - @$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show() - @adjustLevelInfoPosition e - @endHighlight() - @manuallyPositionedLevelInfoID = false - - onMouseLeaveLevel: (e) -> - return if application.isIPadApp - levelID = $(e.target).parents('.level').data('level-id') - return if @manuallyPositionedLevelInfoID and levelID isnt @manuallyPositionedLevelInfoID - @$el.find(".level-info-container[data-level-id='#{levelID}']").hide() - @manuallyPositionedLevelInfoID = null - - onMouseMoveMap: (e) -> - return if application.isIPadApp - @adjustLevelInfoPosition e unless @manuallyPositionedLevelInfoID - - adjustLevelInfoPosition: (e) -> - return unless @$levelInfo - @$map ?= @$el.find('.map') - mapOffset = @$map.offset() - mapX = e.pageX - mapOffset.left - mapY = e.pageY - mapOffset.top - margin = 20 - width = @$levelInfo.outerWidth() - @$levelInfo.css('left', Math.min(Math.max(margin, mapX - width / 2), @$map.width() - width - margin)) - height = @$levelInfo.outerHeight() - top = mapY - @$levelInfo.outerHeight() - 60 - if top < 20 - top = mapY + 60 - @$levelInfo.css('top', top) - - onWindowResize: (e) => - mapHeight = iPadHeight = 1536 - mapWidth = if @terrain is 'dungeon' then 2350 else 2500 - iPadWidth = 2048 - aspectRatio = mapWidth / mapHeight - iPadAspectRatio = iPadWidth / iPadHeight - pageWidth = $(window).width() - pageHeight = $(window).height() - widthRatio = pageWidth / mapWidth - heightRatio = pageHeight / mapHeight - iPadWidthRatio = pageWidth / iPadWidth - if @terrain is 'dungeon' - # Make sure we can see almost the whole map, fading to background in one dimension. - if heightRatio <= iPadWidthRatio - # Full width, full height, left and right margin - resultingHeight = pageHeight - resultingWidth = resultingHeight * aspectRatio - else if iPadWidthRatio < heightRatio * (iPadAspectRatio / aspectRatio) - # Cropped width, full height, left and right margin - resultingWidth = pageWidth - resultingHeight = resultingWidth / aspectRatio - else - # Cropped width, full height, top and bottom margin - resultingWidth = pageWidth * aspectRatio / iPadAspectRatio - resultingHeight = resultingWidth / aspectRatio - else - # Scale it in either dimension so that we're always full on one of the dimensions. - if heightRatio > widthRatio - resultingHeight = pageHeight - resultingWidth = resultingHeight * aspectRatio - else - resultingWidth = pageWidth - resultingHeight = resultingWidth / aspectRatio - resultingMarginX = (pageWidth - resultingWidth) / 2 - resultingMarginY = (pageHeight - resultingHeight) / 2 - @$el.find('.map').css(width: resultingWidth, height: resultingHeight, 'margin-left': resultingMarginX, 'margin-top': resultingMarginY) - - playAmbientSound: -> - return if @ambientSound - return unless file = {dungeon: 'ambient-dungeon', forest: 'ambient-map-grass'}[@terrain] - src = "/file/interface/#{file}#{AudioPlayer.ext}" - unless AudioPlayer.getStatus(src)?.loaded - AudioPlayer.preloadSound src - Backbone.Mediator.subscribeOnce 'audio-player:loaded', @playAmbientSound, @ - return - @ambientSound = createjs.Sound.play src, loop: -1, volume: 0.1 - createjs.Tween.get(@ambientSound).to({volume: 1.0}, 1000) - - playMusic: -> - @musicPlayer = new MusicPlayer() - musicFile = {dungeon: '/music/music-menu-dungeon', forest: '/music/music-menu-grass'}[@terrain] - Backbone.Mediator.publish 'music-player:play-music', play: true, file: musicFile - storage.save("loaded-menu-music-#{@terrain}", true) unless @probablyCachedMusic - - preloadTopHeroes: -> - return # Don't do this because these two have feature images, so we don't need the raw vector data for them. Later they'll all have feature images... - for heroID in ['captain', 'knight'] - url = "/db/thang.type/#{ThangType.heroes[heroID]}/version" - continue if @supermodel.getModel url - fullHero = new ThangType() - fullHero.setURL url - @supermodel.loadModel fullHero, 'thang' - - updateVolume: (volume) -> - volume ?= me.get('volume') ? 1.0 - classes = ['vol-off', 'vol-down', 'vol-up'] - button = $('#volume-button', @$el) - button.toggleClass 'vol-off', volume <= 0.0 - button.toggleClass 'vol-down', 0.0 < volume < 1.0 - button.toggleClass 'vol-up', volume >= 1.0 - createjs.Sound.setVolume(if volume is 1 then 0.6 else volume) # Quieter for now until individual sound FX controls work again. - if volume isnt me.get 'volume' - me.set 'volume', volume - me.patch() - - onToggleVolume: (e) -> - button = $(e.target).closest('#volume-button') - classes = ['vol-off', 'vol-down', 'vol-up'] - volumes = [0, 0.4, 1.0] - for oldClass, i in classes - if button.hasClass oldClass - newI = (i + 1) % classes.length - break - else if i is classes.length - 1 # no oldClass - newI = 2 - @updateVolume volumes[newI] - - -dungeon = [ - { - name: 'Dungeons of Kithgard' - type: 'hero' - id: 'dungeons-of-kithgard' - original: '528110f30268d018e3000001' - description: 'Grab the gem, but touch nothing else. Start here.' - x: 14 - y: 15.5 - nextLevels: - continue: 'gems-in-the-deep' - skip_ahead: 'shadow-guard' - } - { - name: 'Gems in the Deep' - type: 'hero' - id: 'gems-in-the-deep' - original: '54173c90844506ae0195a0b4' - description: 'Quickly collect the gems; you will need them.' - x: 29 - y: 12 - nextLevels: - continue: 'shadow-guard' - skip_ahead: 'forgetful-gemsmith' - } - { - name: 'Shadow Guard' - type: 'hero' - id: 'shadow-guard' - original: '54174347844506ae0195a0b8' - description: 'Evade the Kithgard minion.' - x: 44 - y: 11 - nextLevels: - more_practice: 'kounter-kithwise' - continue: 'forgetful-gemsmith' - } - { - name: 'Kounter Kithwise' - type: 'hero' - id: 'kounter-kithwise' - original: '54527a6257e83800009730c7' - description: 'Practice your evasion skills with more guards.' - x: 55 - y: 11 - nextLevels: - #more_practice: 'crawlways-of-kithgard' - continue: 'forgetful-gemsmith' - practice: true - } - #{ - # name: 'Crawlways of Kithgard' - # type: 'hero' - # # id: 'crawlways-of-kithgard' - # original: '545287ef57e83800009730d5' - # description: 'Dart in and grab the gem–at the right moment.' - # x: 57 - # y: 12 - # nextLevels: - # continue: 'true-names' - # practice: true - #} - { - name: 'Forgetful Gemsmith' - type: 'hero' - id: 'forgetful-gemsmith' - original: '544a98f62d002f0000fe331a' - description: 'Grab even more gems as you practice moving.' - x: 66 - y: 11 - nextLevels: - continue: 'true-names' - } - { - name: 'True Names' - type: 'hero' - id: 'true-names' - original: '541875da4c16460000ab990f' - description: 'Learn an enemy\'s true name to defeat it.' - x: 76 - y: 13 - nextLevels: - more_practice: 'favorable-odds' - continue: 'the-raised-sword' - } - { - name: 'Favorable Odds' - type: 'hero' - id: 'favorable-odds' - original: '5452972f57e83800009730de' - description: 'Test out your battle skills by defeating more munchkins.' - x: 80.85 - y: 16 - nextLevels: - continue: 'the-raised-sword' - practice: true - } - { - name: 'The Raised Sword' - type: 'hero' - id: 'the-raised-sword' - original: '5418aec24c16460000ab9aa6' - description: 'Learn to equip yourself for combat.' - x: 85 - y: 20 - nextLevels: - continue: 'haunted-kithmaze' - } - #{ - # name: 'The First Kithmaze' - # type: 'hero' - # id: 'the-first-kithmaze' - # original: '5418b9d64c16460000ab9ab4' - # description: 'The builders of Kithgard constructed many mazes to confuse travelers.' - # x: 78 - # y: 29 - # nextLevels: - # more_practice: 'descending-further' - # continue: 'the-second-kithmaze' - # skip_ahead: 'dread-door' - #} - { - name: 'Haunted Kithmaze' - type: 'hero' - id: 'haunted-kithmaze' - original: '545a5914d820eb0000f6dc0a' - description: 'The builders of Kithgard constructed many mazes to confuse travelers.' - x: 78 - y: 29 - nextLevels: - more_practice: 'descending-further' - continue: 'the-second-kithmaze' - skip_ahead: 'dread-door' - } - { - name: 'Descending Further' - type: 'hero' - id: 'descending-further' - original: '5452a84d57e83800009730e4' - description: 'Another day, another maze.' - x: 70 - y: 28 - nextLevels: - continue: 'the-second-kithmaze' - practice: true - } - { - name: 'The Second Kithmaze' - type: 'hero' - id: 'the-second-kithmaze' - original: '5418cf256bae62f707c7e1c3' - description: 'Many have tried, few have found their way through this maze.' - x: 58 - y: 23 - nextLevels: - continue: 'dread-door' - } - { - name: 'Dread Door' - type: 'hero' - id: 'dread-door' - original: '5418d40f4c16460000ab9ac2' - description: 'Behind a dread door lies a chest full of riches.' - x: 59 - y: 32 - nextLevels: - continue: 'known-enemy' - } - { - name: 'Known Enemy' - type: 'hero' - id: 'known-enemy' - original: '5452adea57e83800009730ee' - description: 'Begin to use variables in your battles.' - x: 67 - y: 39 - nextLevels: - continue: 'master-of-names' - } - { - name: 'Master of Names' - type: 'hero' - id: 'master-of-names' - original: '5452c3ce57e83800009730f7' - description: 'Use your glasses to defend yourself from the Kithmen.' - x: 75 - y: 46 - nextLevels: - continue: 'lowly-kithmen' - skip_ahead: 'closing-the-distance' - } - { - name: 'Lowly Kithmen' - type: 'hero' - id: 'lowly-kithmen' - original: '541b24511ccc8eaae19f3c1f' - description: 'Now that you can see them, they\'re everywhere!' - x: 85 - y: 40 - nextLevels: - continue: 'closing-the-distance' - skip_ahead: 'the-final-kithmaze' - } - { - name: 'Closing the Distance' - type: 'hero' - id: 'closing-the-distance' - original: '541b288e1ccc8eaae19f3c25' - description: 'Kithmen are not the only ones to stand in your way.' - x: 93 - y: 47 - nextLevels: - more_practice: 'tactical-strike' - continue: 'the-final-kithmaze' - } - { - name: 'Tactical Strike' - type: 'hero' - id: 'tactical-strike' - original: '5452cfa706a59e000067e4f5' - description: 'They\'re, uh, coming right for us! Sneak up behind them.' - x: 88.65 - y: 63.06 - nextLevels: - continue: 'the-final-kithmaze' - practice: true - } - { - name: 'The Final Kithmaze' - type: 'hero' - id: 'the-final-kithmaze' - original: '541b434e1ccc8eaae19f3c33' - description: 'To escape you must find your way through an Elder Kithman\'s maze.' - x: 83 - y: 68 - nextLevels: - more_practice: 'the-gauntlet' - continue: 'kithgard-gates' - } - { - name: 'The Gauntlet' - type: 'hero' - id: 'the-gauntlet' - original: '5452d8b906a59e000067e4fa' - description: 'Rush for the stairs, battling foes at every turn.' - x: 84.89 - y: 73.88 - nextLevels: - continue: 'kithgard-gates' - practice: true - } - { - name: 'Kithgard Gates' - type: 'hero' - id: 'kithgard-gates' - original: '541c9a30c6362edfb0f34479' - description: 'Escape the Kithgard dungeons and don\'t let the guardians get you.' - x: 89 - y: 82 - nextLevels: - continue: 'defense-of-plainswood' - } - { - name: 'Cavern Survival' - type: 'hero-ladder' - id: 'cavern-survival' - original: '544437e0645c0c0000c3291d' - description: 'Stay alive longer than your opponent amidst hordes of ogres!' - disabled: not me.isAdmin() - x: 17.54 - y: 78.39 - } -] - -forest = [ - { - name: 'Defense of Plainswood' - type: 'hero' - id: 'defense-of-plainswood' - original: '541b67f71ccc8eaae19f3c62' - description: 'Protect the peasants from the pursuing ogres.' - nextLevels: - continue: 'winding-trail' - x: 32.63 - y: 53.69 - } - { - name: 'Winding Trail' - type: 'hero' - id: 'winding-trail' - original: '5446cb40ce01c23e05ecf027' - description: 'Stay alive and navigate through the forest.' - nextLevels: - continue: 'endangered-burl' - x: 39.03 - y: 54.97 - } - { - name: 'Endangered Burl' - type: 'hero' - id: 'endangered-burl' - original: '546e97033f1c1c1be898402b' - description: 'Hunt ogres in the woods, but watch out for lumbering beasts.' - nextLevels: - continue: 'village-guard' - x: 41.09 - y: 43.75 - } - { - name: 'Village Guard' - type: 'hero' - id: 'village-guard' - original: '546e91b8a4b7840000ee92dc' - description: 'Defend a village from marauding munchkin mayhem.' - nextLevels: - continue: 'thornbush-farm' - x: 48.09 - y: 42.75 - } - { - name: 'Thornbush Farm' - type: 'hero' - id: 'thornbush-farm' - original: '5447030525cce60000745e2a' - description: 'Determine refugee peasant from ogre when defending the farm.' - nextLevels: - continue: 'back-to-back' - x: 44.09 - y: 57.75 - } - { - name: 'Back to Back' - type: 'hero' - id: 'back-to-back' - original: '5448330517d7283e051f9b9e' - description: 'Patrol the village entrances, but stay defensive.' - nextLevels: - continue: 'ogre-encampment' - x: 40.14 - y: 63.96 - } - { - name: 'Ogre Encampment' - type: 'hero' - id: 'ogre-encampment' - original: '5456b3c8d5ada30000525605' - description: 'Recover stolen treasure from an ogre encampment.' - nextLevels: - continue: 'woodland-cleaver' - x: 46.48 - y: 70.92 - } - { - name: 'Woodland Cleaver' - type: 'hero' - id: 'woodland-cleaver' - original: '5456bb8dd5ada30000525613' - description: 'Use your new cleave ability to fend off munchkins.' - nextLevels: - continue: 'shield-rush' - x: 52.32 - y: 70.80 - } - { - name: 'Shield Rush' - type: 'hero' - id: 'shield-rush' - original: '5459570bb4461871053292f5' - description: 'Combine cleave and shield to endure an ogre onslaught.' - nextLevels: - continue: 'peasant-protection' - x: 58.54 - y: 66.73 - } - - # Warrior branch - { - name: 'Peasant Protection' - type: 'hero' - id: 'peasant-protection' - original: '545ec477e7f60fd6c55760e9' - description: 'Stay close to Victor.' - nextLevels: - continue: 'munchkin-swarm' - x: 64.37 - y: 62.18 - } - { - name: 'Munchkin Swarm' - type: 'hero' - id: 'munchkin-swarm' - original: '545edba9e7f60fd6c5576133' - description: 'Loot a gigantic chest while surrounded by a swarm of ogre munchkins.' - nextLevels: - continue: 'coinucopia' - x: 71.19 - y: 63.61 - } - - # Ranger branch - { - name: 'Munchkin Harvest' - type: 'hero' - id: 'munchkin-harvest' - original: '5470001860f6cc376131525d' - description: 'Join forces with a new hero: Amara Arrowhead.' - nextLevels: - continue: 'swift-dagger' - x: 64.37 - y: 69.18 - } - { - name: 'Swift Dagger' - type: 'hero' - id: 'swift-dagger' - original: '54701f7860f6cc37613152a1' - description: 'Deal damage from a distance with your new hero.' - nextLevels: - continue: 'shrapnel' - x: 66 - y: 75.61 - } - { - name: 'Shrapnel' - type: 'hero' - id: 'shrapnel' - original: '5470291c60f6cc37613152d1' - description: 'Explore the explosive arts.' - nextLevels: - continue: 'coinucopia' - x: 67 - y: 81 - } - - # Wizard branch - { - name: 'Arcane Ally' - type: 'hero' - id: 'arcane-ally' - original: '5470b98ceb739dbc9d2402c7' - description: 'Stand your ground against large ogres with a new hero: Ms. Hushbaum.' - nextLevels: - continue: 'touch-of-death' - x: 64.37 - y: 55.18 - } - { - name: 'Touch of Death' - type: 'hero' - id: 'touch-of-death' - original: '5470ca33eb739dbc9d2402ee' - description: 'Learn your first spell to siphon life from your foes.' - nextLevels: - continue: 'bonemender' - x: 65 - y: 48 - } - { - name: 'Bonemender' - type: 'hero' - id: 'bonemender' - original: '5470d013eb739dbc9d240323' - description: 'Cast regeneration on allied soldiers to withstand a siege.' - nextLevels: - continue: 'coinucopia' - x: 66 - y: 40 - } - - { - name: 'Coinucopia' - type: 'hero' - id: 'coinucopia' - original: '545bb1181e649a4495f887df' - description: 'Start playing in real-time with input flags as you collect gold coins!' - nextLevels: - continue: 'copper-meadows' - x: 77.54 - y: 65.94 - } - { - name: 'Copper Meadows' - type: 'hero' - id: 'copper-meadows' - original: '5462491c688f333d05d8af38' - description: 'This level exercises: if/else, object members, variables, flag placement, and collection.' - nextLevels: - continue: 'drop-the-flag' - x: 77.54 - y: 55.94 - } - { - name: 'Drop the Flag' - type: 'hero' - id: 'drop-the-flag' - original: '54626472f3c64b7b0598590c' - description: 'This level exercises: flag position, object members.' - nextLevels: - continue: 'deadly-pursuit' - x: 77.54 - y: 45.94 - } - { - name: 'Deadly Pursuit' - type: 'hero' - id: 'deadly-pursuit' - original: '54626f270cacde3f055434ac' - description: 'This level exercises: if/else, flag placement and timing, item collection.' - nextLevels: - continue: 'rich-forager' - x: 77.54 - y: 35.94 - } - { - name: 'Rich Forager' - type: 'hero' - id: 'rich-forager' - original: '546283ddfdd66af405fa8209' - description: 'This level exercises: if/else if, collection, combat.' - nextLevels: - continue: 'multiplayer-treasure-grove' - x: 77.54 - y: 25.94 - } - { - name: 'Siege of Stonehold' - type: 'hero' - id: 'siege-of-stonehold' - original: '54712072eb739dbc9d24034b' - description: 'Unlock the desert world, if you are strong enough to win this epic battle!' - #nextLevels: - # continue: '' - disabled: not me.isAdmin() - x: 83.87 - y: 18.89 - } - { - name: 'Multiplayer Treasure Grove' - type: 'hero-ladder' - id: 'multiplayer-treasure-grove' - original: '5469643c37600b40e0e09c5b' - description: 'Mix collection, flags, and combat in this multiplayer coin-gathering arena.' - x: 67.54 - y: 25.94 - } - { - name: 'Dueling Grounds' - type: 'hero-ladder' - id: 'dueling-grounds' - original: '5442ba0e1e835500007eb1c7' - description: 'Battle head-to-head against another hero in this basic beginner combat arena.' - disabled: not me.isAdmin() - x: 25.5 - y: 77.5 - } -] - -WorldMapView.campaigns = campaigns = [ - #{id: 'beginner', name: 'Beginner Campaign', description: '... in which you learn the wizardry of programming.', levels: tutorials, color: "rgb(255, 80, 60)"} - #{id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: arenas, color: "rgb(80, 5, 60)"} - #{id: 'dev', name: 'Random Harder Levels', description: '... in which you learn the interface while doing something a little harder.', levels: experienced, color: "rgb(80, 60, 255)"} - #{id: 'classic_algorithms' ,name: 'Classic Algorithms', description: '... in which you learn the most popular algorithms in Computer Science.', levels: classicAlgorithms, color: "rgb(110, 80, 120)"} - #{id: 'player_created', name: 'Player-Created', description: '... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>.', levels: playerCreated, color: "rgb(160, 160, 180)"} - {id: 'dungeon', name: 'Dungeon Campaign', levels: dungeon } - {id: 'forest', name: 'Forest Campaign', levels: forest } -] diff --git a/app/views/play/common/LadderSubmissionView.coffee b/app/views/play/common/LadderSubmissionView.coffee index c5dfe772c..8c029ef4c 100644 --- a/app/views/play/common/LadderSubmissionView.coffee +++ b/app/views/play/common/LadderSubmissionView.coffee @@ -46,8 +46,13 @@ module.exports = class LadderSubmissionView extends CocoView showLastSubmitted = not (spanClass in ['submitting']) @$el.find('.last-submitted').toggle(showLastSubmitted) + showApologeticSignupModal: -> + AuthModal = require 'views/core/AuthModal' + @openModalView(new AuthModal({showRequiredError: true})) + rankSession: (e) -> return unless @session.readyToRank() + return @showApologeticSignupModal() if me.get('anonymous') @playSound 'menu-button-click' @setRankingButtonText 'submitting' success = => @@ -56,27 +61,26 @@ module.exports = class LadderSubmissionView extends CocoView failure = (jqxhr, textStatus, errorThrown) => console.log jqxhr.responseText @setRankingButtonText 'failed' unless @destroyed - transpiledCode = @transpileSession() + @transpileSession (transpiledCode) => - ajaxData = - session: @session.id - levelID: @level.id - originalLevelID: @level.get('original') - levelMajorVersion: @level.get('version').major - transpiledCode: transpiledCode + ajaxData = + session: @session.id + levelID: @level.id + originalLevelID: @level.get('original') + levelMajorVersion: @level.get('version').major + transpiledCode: transpiledCode - $.ajax '/queue/scoring', { - type: 'POST' - data: ajaxData - success: success - error: failure - } + $.ajax '/queue/scoring', { + type: 'POST' + data: ajaxData + success: success + error: failure + } - transpileSession: -> + transpileSession: (callback) -> submittedCode = @session.get('code') codeLanguage = @session.get('codeLanguage') or 'javascript' @session.set('submittedCodeLanguage', codeLanguage) - @session.save() # TODO: maybe actually use a callback to make sure this works? transpiledCode = {} for thang, spells of submittedCode transpiledCode[thang] = {} @@ -85,7 +89,7 @@ module.exports = class LadderSubmissionView extends CocoView aetherOptions = createAetherOptions functionName: spellID, codeLanguage: codeLanguage aether = new Aether aetherOptions transpiledCode[thang][spellID] = aether.transpile spell - transpiledCode + @session.save null, success: -> callback transpiledCode onHelpSimulate: -> @playSound 'menu-button-click' diff --git a/app/views/play/level/ControlBarView.coffee b/app/views/play/level/ControlBarView.coffee index 23600b61f..2b8d07c94 100644 --- a/app/views/play/level/ControlBarView.coffee +++ b/app/views/play/level/ControlBarView.coffee @@ -2,12 +2,11 @@ CocoView = require 'views/core/CocoView' template = require 'templates/play/level/control_bar' {me} = require 'core/auth' -GameMenuModal = require 'views/game-menu/GameMenuModal' +GameMenuModal = require 'views/play/menu/GameMenuModal' RealTimeModel = require 'models/RealTimeModel' RealTimeCollection = require 'collections/RealTimeCollection' LevelSetupManager = require 'lib/LevelSetupManager' -GameMenuModal = require 'views/game-menu/GameMenuModal' -CampaignOptions = require 'lib/CampaignOptions' +GameMenuModal = require 'views/play/menu/GameMenuModal' module.exports = class ControlBarView extends CocoView id: 'control-bar-view' @@ -26,6 +25,7 @@ module.exports = class ControlBarView extends CocoView 'click .levels-link-area': 'onClickHome' 'click .home a': 'onClickHome' 'click .multiplayer-area': 'onClickMultiplayer' + 'click #control-bar-sign-up-button': 'onClickSignupButton' constructor: (options) -> @worldName = options.worldName @@ -33,9 +33,13 @@ module.exports = class ControlBarView extends CocoView @level = options.level @levelID = @level.get('slug') @spectateGame = options.spectateGame ? false + @observing = options.session.get('creator') isnt me.id super options - if @isMultiplayerLevel = @level.get('type') in ['hero-ladder'] + if @level.get('type') in ['hero-ladder', 'course-ladder'] and me.isAdmin() + @isMultiplayerLevel = true @multiplayerStatusManager = new MultiplayerStatusManager @levelID, @onMultiplayerStateChanged + if @level.get 'replayable' + @listenTo @session, 'change-difficulty', @onSessionDifficultyChanged setBus: (@bus) -> @@ -55,26 +59,40 @@ module.exports = class ControlBarView extends CocoView super c c.worldName = @worldName c.multiplayerEnabled = @session.get('multiplayer') - c.ladderGame = @level.get('type') in ['ladder', 'hero-ladder'] + c.ladderGame = @level.get('type') in ['ladder', 'hero-ladder', 'course-ladder'] if c.isMultiplayerLevel = @isMultiplayerLevel c.multiplayerStatus = @multiplayerStatusManager?.status + if @level.get 'replayable' + c.levelDifficulty = @session.get('state')?.difficulty ? 0 + if @observing + c.levelDifficulty = Math.max 0, c.levelDifficulty - 1 # Show the difficulty they won, not the next one. + c.difficultyTitle = "#{$.i18n.t 'play.level_difficulty'}#{c.levelDifficulty}" + @lastDifficulty = c.levelDifficulty c.spectateGame = @spectateGame + c.observing = @observing @homeViewArgs = [{supermodel: if @hasReceivedMemoryWarning then null else @supermodel}] - if @level.get('type', true) in ['ladder', 'ladder-tutorial', 'hero-ladder'] + if @level.get('type', true) in ['ladder', 'ladder-tutorial', 'hero-ladder', 'course-ladder'] levelID = @level.get('slug').replace /\-tutorial$/, '' - @homeLink = c.homeLink = '/play/ladder/' + levelID - @homeViewClass = require 'views/play/ladder/LadderView' + @homeLink = '/play/ladder/' + levelID + @homeViewClass = 'views/ladder/LadderView' @homeViewArgs.push levelID else if @level.get('type', true) in ['hero', 'hero-coop'] - @homeLink = c.homeLink = '/play' - @homeViewClass = require 'views/play/WorldMapView' - campaign = CampaignOptions.getCampaignForSlug @level.get 'slug' - if campaign isnt 'dungeon' - @homeLink += '/' + campaign - @homeViewArgs.push campaign + @homeLink = '/play' + @homeViewClass = 'views/play/CampaignView' + campaign = @level.get 'campaign' + @homeLink += '/' + campaign + @homeViewArgs.push campaign + else if @level.get('type', true) in ['course', 'course-ladder'] + @homeLink = '/courses/mock1' + @homeViewClass = 'views/courses/mock1/CourseDetailsView' + #campaign = @level.get 'campaign' + #@homeLink += '/' + campaign + #@homeViewArgs.push campaign + @homeLink += '/' + '0' + @homeViewArgs.push '0' else - @homeLink = c.homeLink = '/' - @homeViewClass = require 'views/HomeView' + @homeLink = '/' + @homeViewClass = 'views/HomeView' c.editorLink = "/editor/level/#{@level.get('slug')}" c.homeLink = @homeLink c @@ -95,6 +113,9 @@ module.exports = class ControlBarView extends CocoView onClickMultiplayer: (e) -> @openModalView new GameMenuModal showTab: 'multiplayer', level: @level, session: @session, supermodel: @supermodel + onClickSignupButton: -> + window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Control Bar', level: @levelID + onDisableControls: (e) -> @toggleControls e, false onEnableControls: (e) -> @toggleControls e, true toggleControls: (e, enabled) -> @@ -106,6 +127,10 @@ module.exports = class ControlBarView extends CocoView onIPadMemoryWarning: (e) -> @hasReceivedMemoryWarning = true + onSessionDifficultyChanged: -> + return if @session.get('state')?.difficulty is @lastDifficulty + @render() + destroy: -> @setupManager?.destroy() @multiplayerStatusManager?.destroy() diff --git a/app/views/play/level/LevelDialogueView.coffee b/app/views/play/level/LevelDialogueView.coffee index 238d65418..207bc12b1 100644 --- a/app/views/play/level/LevelDialogueView.coffee +++ b/app/views/play/level/LevelDialogueView.coffee @@ -17,6 +17,11 @@ module.exports = class LevelDialogueView extends CocoView 'click': 'onClick' 'click a': 'onClickLink' + constructor: (options) -> + super options + @level = options.level + @sessionID = options.sessionID + onClick: (e) -> Backbone.Mediator.publish 'tome:focus-editor', {} @@ -33,7 +38,7 @@ module.exports = class LevelDialogueView extends CocoView $('body').addClass('dialogue-view-active') @setMessage e.message, e.mood, e.responses - window.tracker?.trackEvent 'Heard Sprite', {message: e.message, label: e.message}, ['Google Analytics'] + window.tracker?.trackEvent 'Heard Sprite', {message: e.message, label: e.message, ls: @sessionID} onDialogueSoundCompleted: -> @$el.removeClass 'speaking' diff --git a/app/views/play/level/LevelHUDView.coffee b/app/views/play/level/LevelHUDView.coffee index 88479f9e7..d44db0849 100644 --- a/app/views/play/level/LevelHUDView.coffee +++ b/app/views/play/level/LevelHUDView.coffee @@ -1,7 +1,7 @@ CocoView = require 'views/core/CocoView' template = require 'templates/play/level/hud' prop_template = require 'templates/play/level/hud_prop' -LevelOptions = require 'lib/LevelOptions' +utils = require 'core/utils' module.exports = class LevelHUDView extends CocoView id: 'thang-hud' @@ -22,7 +22,7 @@ module.exports = class LevelHUDView extends CocoView afterRender: -> super() @$el.addClass 'no-selection' - if LevelOptions[@options.level.get('slug')]?.hidesHUD + if @options.level.get('hidesHUD') @hidesHUD = true @$el.addClass 'hide-hud-properties' @@ -101,15 +101,15 @@ module.exports = class LevelHUDView extends CocoView createProperties: -> if @thang.id in ['Hero Placeholder', 'Hero Placeholder 1'] - name = {knight: 'Tharin', captain: 'Anya', librarian: 'Hushbaum', sorcerer: 'Pender', 'potion-master': 'Omarn', samurai: 'Hattori', ninja: 'Amara'}[@thang.type] ? 'Hero' + name = {knight: 'Tharin', captain: 'Anya', librarian: 'Hushbaum', sorcerer: 'Pender', 'potion-master': 'Omarn', samurai: 'Hattori', ninja: 'Amara', raider: 'Arryn', goliath: 'Okar', guardian: 'Illia', pixie: 'Zana', assassin: 'Ritic', necromancer: 'Nalfar', 'dark-wizard': 'Usara'}[@thang.type] ? 'Hero' else - name = if @thang.type then "#{@thang.id} - #{@thang.type}" else @thang.id - @$el.find('.thang-name').text name + name = @thang.hudName or (if @thang.type then "#{@thang.id} - #{@thang.type}" else @thang.id) + utils.replaceText @$el.find('.thang-name'), name props = @$el.find('.thang-props') props.find('.prop').remove() #propNames = _.without @thang.hudProperties ? [], 'action' propNames = @thang.hudProperties - for prop, i in propNames + for prop, i in propNames ? [] pel = @createPropElement prop continue unless pel? if pel.find('.bar').is('*') and props.find('.bar').is('*') @@ -129,7 +129,7 @@ module.exports = class LevelHUDView extends CocoView return null # included in the bar context = prop: prop - hasIcon: prop in ['health', 'pos', 'target', 'collectedThangIDs', 'gold', 'bountyGold', 'visualRange', 'attackDamage', 'attackRange', 'maxSpeed', 'attackNearbyEnemyRange'] + hasIcon: prop in ['health', 'pos', 'target', 'collectedThangIDs', 'gold', 'bountyGold', 'value', 'visualRange', 'attackDamage', 'attackRange', 'maxSpeed', 'attackNearbyEnemyRange'] hasBar: prop in ['health'] $(prop_template(context)) @@ -145,7 +145,7 @@ module.exports = class LevelHUDView extends CocoView labelText = prop + ': ' + @formatValue(prop, val) + ' / ' + @formatValue(prop, max) if regen labelText += ' (+' + @formatValue(prop, regen) + '/s)' - pel.find('.bar-prop-value').text(Math.round(max)) if max + utils.replaceText pel.find('.bar-prop-value'), Math.round(val) else s = @formatValue(prop, val) labelText = "#{prop}: #{s}" @@ -153,7 +153,7 @@ module.exports = class LevelHUDView extends CocoView cooldown = @thang.actions.attack.cooldown dps = @thang.attackDamage / cooldown labelText += " / #{cooldown.toFixed(2)}s (DPS: #{dps.toFixed(2)})" - pel.find('.prop-value').text s + utils.replaceText pel.find('.prop-value'), s pel.attr 'title', labelText pel diff --git a/app/views/play/level/LevelLoadingView.coffee b/app/views/play/level/LevelLoadingView.coffee index dc9b72e86..e39339f37 100644 --- a/app/views/play/level/LevelLoadingView.coffee +++ b/app/views/play/level/LevelLoadingView.coffee @@ -1,6 +1,7 @@ CocoView = require 'views/core/CocoView' template = require 'templates/play/level/level_loading' utils = require 'core/utils' +SubscribeModal = require 'views/core/SubscribeModal' module.exports = class LevelLoadingView extends CocoView id: 'level-loading-view' @@ -9,9 +10,12 @@ module.exports = class LevelLoadingView extends CocoView events: 'mousedown .start-level-button': 'startUnveiling' # Split into two for animation smoothness. 'click .start-level-button': 'onClickStartLevel' + 'click .start-subscription-button': 'onClickStartSubscription' subscriptions: 'level:loaded': 'onLevelLoaded' # If Level loads after level loading view. + 'level:subscription-required': 'onSubscriptionRequired' # If they'd need a subscription to start playing. + 'subscribe-modal:subscribed': 'onSubscribed' shortcuts: 'enter': 'onEnterPressed' @@ -42,9 +46,9 @@ module.exports = class LevelLoadingView extends CocoView goalContainer = @$el.find('.level-loading-goals') goalList = goalContainer.find('ul') goalCount = 0 - for goalID, goal of @level.get('goals') when (not goal.team or goal.team is e.team) and not goal.hiddenGoal + for goalID, goal of @level.get('goals') when (not goal.team or goal.team is (e.team or 'humans')) and not goal.hiddenGoal name = utils.i18n goal, 'name' - goalList.append $('<li class="list-group-item">' + name + '</li>') + goalList.append $('<li>' + name + '</li>') ++goalCount if goalCount goalContainer.removeClass('secret') @@ -68,8 +72,8 @@ module.exports = class LevelLoadingView extends CocoView @unveil() else Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'level_loaded', volume: 0.75 # old: loading_ready - @$el.find('.progress').addClass 'active progress-striped' - @$el.find('.start-level-button').removeClass 'secret' + @$el.find('.progress').hide() + @$el.find('.start-level-button').show() startUnveiling: (e) -> @playSound 'menu-button-click' @@ -95,7 +99,19 @@ module.exports = class LevelLoadingView extends CocoView @$el.find('.right-wing').css right: '-100%', backgroundPosition: 'left -400px top 0' Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'loading-view-unveil', volume: 0.5 _.delay @onUnveilEnded, duration * 1000 + $('#level-footer-background').detach().appendTo('#page-container').slideDown(duration * 1000) onUnveilEnded: => return if @destroyed Backbone.Mediator.publish 'level:loading-view-unveiled', view: @ + + onSubscriptionRequired: (e) -> + @$el.find('.level-loading-goals, .tip, .load-progress').hide() + @$el.find('.subscription-required').show() + + onClickStartSubscription: (e) -> + @openModalView new SubscribeModal() + window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'level loading', level: @level?.get('slug') or @options.level?.get('slug') + + onSubscribed: -> + document.location.reload() diff --git a/app/views/play/level/LevelPlaybackView.coffee b/app/views/play/level/LevelPlaybackView.coffee index 70d61d37f..213f98c96 100644 --- a/app/views/play/level/LevelPlaybackView.coffee +++ b/app/views/play/level/LevelPlaybackView.coffee @@ -1,7 +1,6 @@ CocoView = require 'views/core/CocoView' template = require 'templates/play/level/playback' {me} = require 'core/auth' -LevelOptions = require 'lib/LevelOptions' module.exports = class LevelPlaybackView extends CocoView id: 'playback-view' @@ -67,7 +66,7 @@ module.exports = class LevelPlaybackView extends CocoView @goto = t 'play_level.time_goto' @current = t 'play_level.time_current' @total = t 'play_level.time_total' - @$el.find('#play-button').css('visibility', 'hidden') if LevelOptions[@options.levelID]?.hidesPlayButton # Don't show for first few levels, confuses new players. + @$el.find('#play-button').css('visibility', 'hidden') if @options.level.get 'hidesPlayButton' # Don't show for first few levels, confuses new players. updatePopupContent: -> @timePopup?.updateContent "<h2>#{@timeToString @newTime}</h2>#{@formatTime(@current, @currentTime)}<br/>#{@formatTime(@total, @totalTime)}" @@ -110,7 +109,6 @@ module.exports = class LevelPlaybackView extends CocoView @togglePlaybackControls false Backbone.Mediator.publish 'playback:real-time-playback-started', {} Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'real-time-playback-start', volume: 1 - Backbone.Mediator.publish 'level:set-letterbox', on: true if @options.level.get('type', true) is ['hero'] # not with flags...? onRealTimeMultiplayerCast: (e) -> @realTime = true @@ -391,7 +389,7 @@ class HoverPopup extends $.fn.popover.Constructor calculatedOffset = top: pos.top - actualHeight left: pos.left + pos.width / 2 - actualWidth / 2 - this.applyPlacement(calculatedOffset, 'top') + @applyPlacement(calculatedOffset, 'top') getPosition: -> top: @$element.offset().top diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index c2c8ef10f..64cdf9b49 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -36,6 +36,7 @@ VictoryModal = require './modal/VictoryModal' HeroVictoryModal = require './modal/HeroVictoryModal' InfiniteLoopModal = require './modal/InfiniteLoopModal' LevelSetupManager = require 'lib/LevelSetupManager' +ContactModal = require 'views/core/ContactModal' PROFILE_ME = false @@ -61,13 +62,10 @@ module.exports = class PlayLevelView extends RootView 'god:infinite-loop': 'onInfiniteLoop' 'level:reload-from-data': 'onLevelReloadFromData' 'level:reload-thang-type': 'onLevelReloadThangType' - 'level:play-next-level': 'onPlayNextLevel' - 'level:edit-wizard-settings': 'showWizardSettingsModal' 'level:session-will-save': 'onSessionWillSave' 'level:started': 'onLevelStarted' 'level:loading-view-unveiling': 'onLoadingViewUnveiling' 'level:loading-view-unveiled': 'onLoadingViewUnveiled' - 'level:loaded': 'onLevelLoaded' 'level:session-loaded': 'onSessionLoaded' 'playback:real-time-playback-waiting': 'onRealTimePlaybackWaiting' 'playback:real-time-playback-started': 'onRealTimePlaybackStarted' @@ -83,6 +81,7 @@ module.exports = class PlayLevelView extends RootView 'click #level-done-button': 'onDonePressed' 'click #stop-real-time-playback-button': -> Backbone.Mediator.publish 'playback:stop-real-time-playback', {} 'click #fullscreen-editor-background-screen': (e) -> Backbone.Mediator.publish 'tome:toggle-maximize', {} + 'click .contact-link': 'onContactClicked' shortcuts: 'ctrl+s': 'onCtrlS' @@ -96,6 +95,7 @@ module.exports = class PlayLevelView extends RootView @isEditorPreview = @getQueryVariable 'dev' @sessionID = @getQueryVariable 'session' + @observing = @getQueryVariable 'observing' @opponentSessionID = @getQueryVariable('opponent') @opponentSessionID ?= @options.opponent @@ -103,6 +103,8 @@ module.exports = class PlayLevelView extends RootView $(window).on 'resize', @onWindowResize @saveScreenshot = _.throttle @saveScreenshot, 30000 + application.tracker?.enableInspectletJS(@levelID) + if @isEditorPreview @supermodel.shouldSaveBackups = (model) -> # Make sure to load possibly changed things from localStorage. model.constructor.className in ['Level', 'LevelComponent', 'LevelSystem', 'ThangType'] @@ -110,7 +112,7 @@ module.exports = class PlayLevelView extends RootView setTimeout f, 100 else @load() - application.tracker?.trackEvent 'Started Level Load', category: 'Play Level', level: @levelID, label: @levelID, ['Google Analytics'] + application.tracker?.trackEvent 'Started Level Load', category: 'Play Level', level: @levelID, label: @levelID unless @observing setLevel: (@level, givenSupermodel) -> @supermodel.models = givenSupermodel.models @@ -127,7 +129,7 @@ module.exports = class PlayLevelView extends RootView load: -> @loadStartTime = new Date() @god = new God debugWorker: true - @levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, sessionID: @sessionID, opponentSessionID: @opponentSessionID, team: @getQueryVariable('team') + @levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, sessionID: @sessionID, opponentSessionID: @opponentSessionID, team: @getQueryVariable('team'), observing: @observing @listenToOnce @levelLoader, 'world-necessities-loaded', @onWorldNecessitiesLoaded trackLevelLoadEnd: -> @@ -135,8 +137,9 @@ module.exports = class PlayLevelView extends RootView @loadEndTime = new Date() loadDuration = @loadEndTime - @loadStartTime console.debug "Level unveiled after #{(loadDuration / 1000).toFixed(2)}s" - application.tracker?.trackEvent 'Finished Level Load', category: 'Play Level', label: @levelID, level: @levelID, loadDuration: loadDuration, ['Google Analytics'] - application.tracker?.trackTiming loadDuration, 'Level Load Time', @levelID, @levelID + unless @observing + application.tracker?.trackEvent 'Finished Level Load', category: 'Play Level', label: @levelID, level: @levelID, loadDuration: loadDuration + application.tracker?.trackTiming loadDuration, 'Level Load Time', @levelID, @levelID # CocoView overridden methods ############################################### @@ -148,7 +151,7 @@ module.exports = class PlayLevelView extends RootView afterRender: -> super() window.onPlayLevelViewLoaded? @ # still a hack - @insertSubView @loadingView = new LevelLoadingView autoUnveil: @options.autoUnveil, level: @level # May not have @level loaded yet + @insertSubView @loadingView = new LevelLoadingView autoUnveil: @options.autoUnveil or @observing, level: @levelLoader?.level ? @level # May not have @level loaded yet @$el.find('#level-done-button').hide() $('body').addClass('is-playing') $('body').bind('touchmove', false) if @isIPadApp() @@ -179,7 +182,7 @@ module.exports = class PlayLevelView extends RootView @session = @levelLoader.session @world = @levelLoader.world @level = @levelLoader.level - @$el.addClass 'hero' if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] + @$el.addClass 'hero' if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] @$el.addClass 'flags' if _.any(@world.thangs, (t) -> (t.programmableProperties and 'findFlags' in t.programmableProperties) or t.inventory?.flag) or @level.get('slug') is 'sky-span' # TODO: Update terminology to always be opponentSession or otherSession # TODO: E.g. if it's always opponent right now, then variable names should be opponentSession until we have coop play @@ -234,15 +237,15 @@ module.exports = class PlayLevelView extends RootView @god.setGoalManager @goalManager insertSubviews: -> - @insertSubView @tome = new TomeView levelID: @levelID, session: @session, otherSession: @otherSession, thangs: @world.thangs, supermodel: @supermodel, level: @level - @insertSubView new LevelPlaybackView session: @session, levelID: @levelID, level: @level + @insertSubView @tome = new TomeView levelID: @levelID, session: @session, otherSession: @otherSession, thangs: @world.thangs, supermodel: @supermodel, level: @level, observing: @observing + @insertSubView new LevelPlaybackView session: @session, level: @level @insertSubView new GoalsView {} @insertSubView new LevelFlagsView levelID: @levelID, world: @world if @$el.hasClass 'flags' @insertSubView new GoldView {} @insertSubView new HUDView {level: @level} - @insertSubView new LevelDialogueView {level: @level} + @insertSubView new LevelDialogueView {level: @level, sessionID: @session.id} @insertSubView new ChatView levelID: @levelID, sessionID: @session.id, session: @session - @insertSubView new ProblemAlertView {} + @insertSubView new ProblemAlertView session: @session, level: @level, supermodel: @supermodel worldName = utils.i18n @level.attributes, 'name' @controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel} #_.delay (=> Backbone.Mediator.publish('level:set-debug', debug: true)), 5000 if @isIPadApp() # if me.displayName() is 'Nick' @@ -260,22 +263,26 @@ module.exports = class PlayLevelView extends RootView @bus = LevelBus.get(@levelID, @session.id) @bus.setSession(@session) @bus.setSpells @tome.spells + if @session.get('multiplayer') and not me.isAdmin() + @session.set 'multiplayer', false # Temp: multiplayer has bugged out some sessions, so ignoring it. @bus.connect() if @session.get('multiplayer') # Load Completed Setup ###################################################### - onLevelLoaded: (e) -> - # Just the level has been loaded by the level loader - @showWizardSettingsModal() if not me.get('name') and not @isIPadApp() and not (e.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop']) - onSessionLoaded: (e) -> + Backbone.Mediator.publish "ipad:language-chosen", language: e.session.get('codeLanguage') ? "python" # Just the level and session have been loaded by the level loader - if e.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] and not _.size e.session.get('heroConfig')?.inventory ? {} + if e.level.get('slug') is 'zero-sum' + sorcerer = '52fd1524c7e6cf99160e7bc9' + if e.session.get('creator') is '532dbc73a622924444b68ed9' # Wizard Dude gets his own avatar + sorcerer = '53e126a4e06b897606d38bef' + e.session.set 'heroConfig', {"thangType":sorcerer,"inventory":{"misc-0":"53e2396a53457600003e3f0f","programming-book":"546e266e9df4a17d0d449be5","minion":"54eb5dbc49fa2d5c905ddf56","feet":"53e214f153457600003e3eab","right-hand":"54eab7f52b7506e891ca7202","left-hand":"5463758f3839c6e02811d30f","wrists":"54693797a2b1f53ce79443e9","gloves":"5469425ca2b1f53ce7944421","torso":"546d4a549df4a17d0d449a97","neck":"54693274a2b1f53ce79443c9","eyes":"546941fda2b1f53ce794441d","head":"546d4ca19df4a17d0d449abf"}} + else if e.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] and not _.size e.session.get('heroConfig')?.inventory ? {} @setupManager?.destroy() @setupManager = new LevelSetupManager({supermodel: @supermodel, levelID: @levelID, parent: @, session: @session}) @setupManager.open() - @onRealTimeMultiplayerLevelLoaded e.session if e.level.get('type') in ['hero-ladder'] + @onRealTimeMultiplayerLevelLoaded e.session if e.level.get('type') in ['hero-ladder', 'course-ladder'] onLoaded: -> _.defer => @onLevelLoaderLoaded() @@ -285,9 +292,10 @@ module.exports = class PlayLevelView extends RootView return unless @levelLoader.progress() is 1 # double check, since closing the guide may trigger this early # Save latest level played. - if not (@levelLoader.level.get('type') in ['ladder', 'ladder-tutorial']) + if not @observing and not (@levelLoader.level.get('type') in ['ladder', 'ladder-tutorial']) me.set('lastLevel', @levelID) me.save() + application.tracker?.identify() @saveRecentMatch() if @otherSession @levelLoader.destroy() @levelLoader = null @@ -304,12 +312,19 @@ module.exports = class PlayLevelView extends RootView initSurface: -> webGLSurface = $('canvas#webgl-surface', @$el) normalSurface = $('canvas#normal-surface', @$el) - @surface = new Surface(@world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, wizards: not (@level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'])) + @surface = new Surface(@world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, observing: @observing, playerNames: @findPlayerNames()) worldBounds = @world.getBounds() bounds = [{x: worldBounds.left, y: worldBounds.top}, {x: worldBounds.right, y: worldBounds.bottom}] @surface.camera.setBounds(bounds) @surface.camera.zoomTo({x: 0, y: 0}, 0.1, 0) + findPlayerNames: -> + return {} unless @observing + playerNames = {} + for session in [@session, @otherSession] when session?.get('team') + playerNames[session.get('team')] = session.get('creatorName') or 'Anoner' + playerNames + # Once Surface is Loaded #################################################### onLevelStarted: -> @@ -319,10 +334,7 @@ module.exports = class PlayLevelView extends RootView if window.currentModal and not window.currentModal.destroyed and window.currentModal.constructor isnt VictoryModal return Backbone.Mediator.subscribeOnce 'modal:closed', @onLevelStarted, @ @surface.showLevel() - if @otherSession and not (@level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop']) - # TODO: colorize name and cloud by team, colorize wizard by user's color config - @surface.createOpponentWizard id: @otherSession.get('creator'), name: @otherSession.get('creatorName'), team: @otherSession.get('team'), levelSlug: @level.get('slug'), codeLanguage: @otherSession.get('submittedCodeLanguage') - if @isEditorPreview + if @isEditorPreview or @observing @loadingView.startUnveiling() @loadingView.unveil() @@ -337,9 +349,12 @@ module.exports = class PlayLevelView extends RootView if @options.realTimeMultiplayerSessionID? Backbone.Mediator.publish 'playback:real-time-playback-waiting', {} @realTimeMultiplayerContinueGame @options.realTimeMultiplayerSessionID - application.tracker?.trackEvent 'Started Level', category:'Play Level', levelID: @levelID + # TODO: Is it possible to create a Mongoose ObjectId for 'ls', instead of the string returned from get()? + application.tracker?.trackEvent 'Started Level', category:'Play Level', levelID: @levelID, ls: @session?.get('_id') unless @observing + $(window).trigger 'resize' playAmbientSound: -> + return if @destroyed return if @ambientSound return unless file = {Dungeon: 'ambient-dungeon', Grass: 'ambient-grass'}[@level.get('terrain')] src = "/file/interface/#{file}#{AudioPlayer.ext}" @@ -354,7 +369,7 @@ module.exports = class PlayLevelView extends RootView return if @alreadyLoadedState @alreadyLoadedState = true state = @originalSessionState - if not @level or @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] + if not @level or @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] Backbone.Mediator.publish 'level:suppress-selection-sounds', suppress: true Backbone.Mediator.publish 'tome:select-primary-sprite', {} Backbone.Mediator.publish 'level:suppress-selection-sounds', suppress: false @@ -362,13 +377,9 @@ module.exports = class PlayLevelView extends RootView Backbone.Mediator.publish 'level:set-time', time: 0 Backbone.Mediator.publish 'level:set-playing', playing: true else - if state.frame and @level.get('type', true) isnt 'ladder' # https://github.com/codecombat/codecombat/issues/714 - Backbone.Mediator.publish 'level:set-time', time: 0, frameOffset: state.frame if state.selected # TODO: Should also restore selected spell here by saving spellName Backbone.Mediator.publish 'level:select-sprite', thangID: state.selected, spellName: null - if state.playing? - Backbone.Mediator.publish 'level:set-playing', playing: state.playing # callbacks @@ -412,59 +423,40 @@ module.exports = class PlayLevelView extends RootView onDonePressed: -> @showVictory() onShowVictory: (e) -> - $('#level-done-button').show() unless @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] + $('#level-done-button').show() unless @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] @showVictory() if e.showModal - setTimeout(@preloadNextLevel, 3000) return if @victorySeen @victorySeen = true victoryTime = (new Date()) - @loadEndTime - if victoryTime > 10 * 1000 # Don't track it if we're reloading an already-beaten level + if not @observing and victoryTime > 10 * 1000 # Don't track it if we're reloading an already-beaten level application.tracker?.trackEvent 'Saw Victory', category: 'Play Level' level: @level.get('name') label: @level.get('name') - application.tracker?.trackTiming victoryTime, 'Level Victory Time', @levelID, @levelID, 100 + levelID: @levelID + ls: @session?.get('_id') + application.tracker?.trackTiming victoryTime, 'Level Victory Time', @levelID, @levelID showVictory: -> + return if @level.hasLocalChanges() # Don't award achievements when beating level changed in level editor @endHighlight() options = {level: @level, supermodel: @supermodel, session: @session, hasReceivedMemoryWarning: @hasReceivedMemoryWarning} - ModalClass = if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] then HeroVictoryModal else VictoryModal + ModalClass = if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] then HeroVictoryModal else VictoryModal victoryModal = new ModalClass(options) @openModalView(victoryModal) if me.get('anonymous') - window.nextLevelURL = @getNextLevelURL() # Signup will go here on completion instead of reloading. + window.nextURL = '/play/' + (@level.get('campaign') ? '') # Signup will go here on completion instead of reloading. onRestartLevel: -> @tome.reloadAllCode() Backbone.Mediator.publish 'level:restarted', {} $('#level-done-button', @$el).hide() - application.tracker?.trackEvent 'Confirmed Restart', category: 'Play Level', level: @level.get('name'), label: @level.get('name') + application.tracker?.trackEvent 'Confirmed Restart', category: 'Play Level', level: @level.get('name'), label: @level.get('name') unless @observing onInfiniteLoop: (e) -> return unless e.firstWorld - @openModalView new InfiniteLoopModal() - application.tracker?.trackEvent 'Saw Initial Infinite Loop', category: 'Play Level', level: @level.get('name'), label: @level.get('name') - - onPlayNextLevel: -> - nextLevelID = @getNextLevelID() - nextLevelURL = @getNextLevelURL() - Backbone.Mediator.publish 'router:navigate', { - route: nextLevelURL, - viewClass: PlayLevelView, - viewArgs: [{supermodel: if @hasReceivedMemoryWarning then null else @supermodel}, nextLevelID]} - - getNextLevel: -> - return null unless nextLevelOriginal = @level.get('nextLevel')?.original - levels = @supermodel.getModels(Level) - return l for l in levels when l.get('original') is nextLevelOriginal - - getNextLevelID: -> - return null unless nextLevel = @getNextLevel() - nextLevelID = nextLevel.get('slug') or nextLevel.id - - getNextLevelURL: -> - return null unless @getNextLevelID() - "/play/level/#{@getNextLevelID()}" + @openModalView new InfiniteLoopModal nonUserCodeProblem: e.nonUserCodeProblem + application.tracker?.trackEvent 'Saw Initial Infinite Loop', category: 'Play Level', level: @level.get('name'), label: @level.get('name') unless @observing onHighlightDOM: (e) -> @highlightElement e.selector, delay: e.delay, sides: e.sides, offset: e.offset, rotation: e.rotation @@ -479,14 +471,6 @@ module.exports = class PlayLevelView extends RootView @bus.removeFirebaseData => @bus.disconnect() - preloadNextLevel: => - # TODO: Loading models in the middle of gameplay causes stuttering. Most of the improvement in loading time is simply from passing the supermodel from this level to the next, but if we can find a way to populate the level early without it being noticeable, that would be even better. -# return if @destroyed -# return if @preloaded -# nextLevel = @getNextLevel() -# @supermodel.populateModel nextLevel -# @preloaded = true - onSessionWillSave: (e) -> # Something interesting has happened, so (at a lower frequency), we'll save a screenshot. #@saveScreenshot e.session @@ -496,6 +480,20 @@ module.exports = class PlayLevelView extends RootView return unless screenshot = @surface?.screenshot() session.save {screenshot: screenshot}, {patch: true, type: 'PUT'} + onContactClicked: (e) -> + @openModalView contactModal = new ContactModal() + screenshot = @surface.screenshot(1, 'image/png', 1.0, 1) + body = + b64png: screenshot.replace 'data:image/png;base64,', '' + filename: "screenshot-#{@levelID}-#{_.string.slugify((new Date()).toString())}.png" + path: "db/user/#{me.id}" + mimetype: 'image/png' + contactModal.screenshotURL = "http://codecombat.com/file/#{body.path}/#{body.filename}" + window.screenshot = screenshot + window.screenshotURL = contactModal.screenshotURL + $.ajax '/file', type: 'POST', data: body, success: (e) -> + contactModal.updateScreenshot?() + # Dynamic sound loading onNewWorld: (e) -> @@ -539,9 +537,15 @@ module.exports = class PlayLevelView extends RootView onSubmissionComplete: => return if @destroyed + return if @level.hasLocalChanges() # Don't award achievements when beating level changed in level editor # TODO: Show a victory dialog specific to hero-ladder level if @goalManager.checkOverallStatus() is 'success' and not @options.realTimeMultiplayerSessionID? - Backbone.Mediator.publish 'level:show-victory', showModal: true + showModalFn = -> Backbone.Mediator.publish 'level:show-victory', showModal: true + @session.recordScores @world.scores, @level + if @level.get 'replayable' + @session.increaseDifficulty showModalFn + else + showModalFn() destroy: -> @levelLoader?.destroy() @@ -557,9 +561,10 @@ module.exports = class PlayLevelView extends RootView delete window.world # not sure where this is set, but this is one way to clean it up @bus?.destroy() #@instance.save() unless @instance.loading - delete window.nextLevelURL + delete window.nextURL console.profileEnd?() if PROFILE_ME @onRealTimeMultiplayerLevelUnloaded() + application.tracker?.disableInspectletJS() super() onIPadMemoryWarning: (e) -> diff --git a/app/views/play/level/modal/HeroVictoryModal.coffee b/app/views/play/level/modal/HeroVictoryModal.coffee index 038ade544..85bff49ca 100644 --- a/app/views/play/level/modal/HeroVictoryModal.coffee +++ b/app/views/play/level/modal/HeroVictoryModal.coffee @@ -1,4 +1,5 @@ ModalView = require 'views/core/ModalView' +AuthModal = require 'views/core/AuthModal' template = require 'templates/play/level/modal/hero-victory-modal' Achievement = require 'models/Achievement' EarnedAchievement = require 'models/EarnedAchievement' @@ -8,7 +9,10 @@ utils = require 'core/utils' ThangType = require 'models/ThangType' LadderSubmissionView = require 'views/play/common/LadderSubmissionView' AudioPlayer = require 'lib/AudioPlayer' -CampaignOptions = require 'lib/CampaignOptions' +User = require 'models/User' +utils = require 'core/utils' +Level = require 'models/Level' +LevelFeedback = require 'models/LevelFeedback' module.exports = class HeroVictoryModal extends ModalView id: 'hero-victory-modal' @@ -21,33 +25,79 @@ module.exports = class HeroVictoryModal extends ModalView events: 'click #continue-button': 'onClickContinue' - 'click .next-level-branch-button': 'onClickNextLevelBranch' + 'click .leaderboard-button': 'onClickLeaderboard' + 'click .return-to-course-button': 'onClickReturnToCourse' 'click .return-to-ladder-button': 'onClickReturnToLadder' + 'click .sign-up-button': 'onClickSignupButton' + 'click .continue-from-offer-button': 'onClickContinueFromOffer' + + # Feedback events + 'mouseover .rating i': (e) -> @showStars(@starNum($(e.target))) + 'mouseout .rating i': -> @showStars() + 'click .rating i': (e) -> + @setStars(@starNum($(e.target))) + @$el.find('.review, .review-label').show() + 'keypress .review textarea': -> @saveReviewEventually() constructor: (options) -> super(options) @session = options.session @level = options.level - achievements = new CocoCollection([], { - url: "/db/achievement?related=#{@session.get('level').original}" - model: Achievement - }) @thangTypes = {} - @achievements = @supermodel.loadCollection(achievements, 'achievements').model - @listenToOnce @achievements, 'sync', @onAchievementsLoaded - @readyToContinue = false - @waitingToContinueSince = new Date() + if @level.get('type', true) is 'hero' + achievements = new CocoCollection([], { + url: "/db/achievement?related=#{@session.get('level').original}" + model: Achievement + }) + @achievements = @supermodel.loadCollection(achievements, 'achievements').model + @listenToOnce @achievements, 'sync', @onAchievementsLoaded + @readyToContinue = false + @waitingToContinueSince = new Date() + @previousXP = me.get 'points', true + @previousLevel = me.level() + else + @readyToContinue = true Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'victory' + if @level.get('type', true) is 'course' and nextLevel = @level.get('nextLevel') + @nextLevel = new Level().setURL "/db/level/#{nextLevel.original}/version/#{nextLevel.majorVersion}" + @nextLevel = @supermodel.loadModel(@nextLevel, 'level').model + if @level.get('type', true) in ['course', 'course-ladder'] + @saveReviewEventually = _.debounce(@saveReviewEventually, 2000) + @loadExistingFeedback() destroy: -> clearInterval @sequentialAnimationInterval + @saveReview() if @$el.find('.review textarea').val() + @feedback?.off() super() onHidden: -> Backbone.Mediator.publish 'music-player:exit-menu', {} super() + loadExistingFeedback: -> + url = "/db/level/#{@level.id}/feedback" + @feedback = new LevelFeedback() + @feedback.setURL url + @feedback.fetch cache: false + @listenToOnce(@feedback, 'sync', -> @onFeedbackLoaded()) + @listenToOnce(@feedback, 'error', -> @onFeedbackNotFound()) + + onFeedbackLoaded: -> + @feedback.url = -> '/db/level.feedback/' + @id + @$el.find('.review textarea').val(@feedback.get('review')) + @$el.find('.review, .review-label').show() + @showStars() + + onFeedbackNotFound: -> + @feedback = new LevelFeedback() + @feedback.set('levelID', @level.get('slug') or @level.id) + @feedback.set('levelName', @level.get('name') or '') + @feedback.set('level', {majorVersion: @level.get('version').major, original: @level.get('original')}) + @showStars() + onAchievementsLoaded: -> + @$el.toggleClass 'full-achievements', @achievements.models.length is 3 thangTypeOriginals = [] achievementIDs = [] for achievement in @achievements.models @@ -61,7 +111,8 @@ module.exports = class HeroVictoryModal extends ModalView for thangTypeOriginal in thangTypeOriginals thangType = new ThangType() thangType.url = "/db/thang.type/#{thangTypeOriginal}/version" - thangType.project = ['original', 'rasterIcon', 'name', 'soundTriggers'] + #thangType.project = ['original', 'rasterIcon', 'name', 'soundTriggers', 'i18n'] # This is what we need, but the PlayHeroesModal needs more, and so we load more to fill up the supermodel. + thangType.project = ['original', 'rasterIcon', 'name', 'slug', 'soundTriggers', 'featureImages', 'gems', 'heroClass', 'description', 'components', 'extendedName', 'unlockLevelName', 'i18n'] @thangTypes[thangTypeOriginal] = @supermodel.loadModel(thangType, 'thang').model @newEarnedAchievements = [] @@ -76,22 +127,42 @@ module.exports = class HeroVictoryModal extends ModalView @newEarnedAchievements.push ea @listenToOnce ea, 'sync', -> if _.all((ea.id for ea in @newEarnedAchievements)) + @newEarnedAchievementsResource.markLoaded() @listenToOnce me, 'sync', -> @readyToContinue = true @updateSavingProgressStatus() - me.fetch() unless me.loading + me.fetch cache: false unless me.loading @readyToContinue = true if not @achievements.models.length + # have to use a something resource because addModelResource doesn't handle models being upserted/fetched via POST like we're doing here + @newEarnedAchievementsResource = @supermodel.addSomethingResource('earned achievements') if @newEarnedAchievements.length + getRenderData: -> c = super() c.levelName = utils.i18n @level.attributes, 'name' - earnedAchievementMap = _.indexBy(@earnedAchievements?.models or [], (ea) -> ea.get('achievement')) - for achievement in @achievements.models + if @level.get('type', true) isnt 'hero' + c.victoryText = utils.i18n @level.get('victory') ? {}, 'body' + earnedAchievementMap = _.indexBy(@newEarnedAchievements or [], (ea) -> ea.get('achievement')) + for achievement in (@achievements?.models or []) earnedAchievement = earnedAchievementMap[achievement.id] if earnedAchievement - achievement.completedAWhileAgo = new Date() - Date.parse(earnedAchievement.get('created')) > 30 * 1000 - c.achievements = @achievements.models + achievement.completedAWhileAgo = new Date().getTime() - Date.parse(earnedAchievement.get('created')) > 30 * 1000 + achievement.worth = achievement.get 'worth', true + achievement.gems = achievement.get('rewards')?.gems + c.achievements = @achievements?.models.slice() or [] + for achievement in c.achievements + achievement.description = utils.i18n achievement.attributes, 'description' + continue unless @supermodel.finished() and proportionalTo = achievement.get 'proportionalTo' + # For repeatable achievements, we modify their base worth/gems by their repeatable growth functions. + achievedAmount = utils.getByPath @session.attributes, proportionalTo + previousAmount = Math.max(0, achievedAmount - 1) + func = achievement.getExpFunction() + achievement.previousWorth = (achievement.get('worth') ? 0) * func previousAmount + achievement.worth = (achievement.get('worth') ? 0) * func achievedAmount + rewards = achievement.get 'rewards' + achievement.gems = rewards?.gems * func achievedAmount if rewards?.gems + achievement.previousGems = rewards?.gems * func previousAmount if rewards?.gems # for testing the three states #if c.achievements.length @@ -107,16 +178,9 @@ module.exports = class HeroVictoryModal extends ModalView c.thangTypes = @thangTypes c.me = me - c.readyToRank = @level.get('type', true) is 'hero-ladder' and @session.readyToRank() + c.readyToRank = @level.get('type', true) in ['hero-ladder', 'course-ladder'] and @session.readyToRank() c.level = @level - @continueLevelLink = @getNextLevelLink 'continue' - @morePracticeLevelLink = @getNextLevelLink 'more_practice' - @skipAheadLevelLink = @getNextLevelLink 'skip_ahead' - c.continueButtons = [ - {key: 'skip_ahead', link: @skipAheadLevelLink, 'choice-explicit': 'skip', 'choice-implicit': 'too_easy'} - {key: 'continue', link: @continueLevelLink, 'choice-explicit': 'next_level', 'choice-implicit': 'just_right'} - {key: 'more_practice', link: @morePracticeLevelLink, 'choice-explicit': 'more_practice', 'choice-implicit': 'too_hard'} - ] + c.i18n = utils.i18n elapsed = (new Date() - new Date(me.get('dateCreated'))) isHourOfCode = me.get('hourOfCode') or elapsed < 120 * 60 * 1000 @@ -130,17 +194,30 @@ module.exports = class HeroVictoryModal extends ModalView $('body').append($('<img src="http://code.org/api/hour/finish_codecombat.png" style="visibility: hidden;">')) me.set 'hourOfCodeComplete', true # Note that this will track even for players who don't have hourOfCode set. me.patch() - window.tracker?.trackEvent 'Hour of Code Finish', {} + window.tracker?.trackEvent 'Hour of Code Finish' # Show the "I'm done" button between 30 - 120 minutes if they definitely came from Hour of Code c.showHourOfCodeDoneButton = me.get('hourOfCode') and showDone + c.showLeaderboard = @level.get('scoreTypes')?.length > 0 and @level.get('type', true) isnt 'course' + + c.showReturnToCourse = not c.showLeaderboard and not me.get('anonymous') and @level.get('type', true) in ['course', 'course-ladder'] + return c afterRender: -> super() + @$el.toggleClass 'show-achievements', @level.get('type', true) is 'hero' return unless @supermodel.finished() @playSelectionSound hero, true for original, hero of @thangTypes # Preload them @updateSavingProgressStatus() + @initializeAnimations() + if @level.get('type', true) in ['hero-ladder', 'course-ladder'] + @ladderSubmissionView = new LadderSubmissionView session: @session, level: @level + @insertSubView @ladderSubmissionView, @$el.find('.ladder-submission-view') + + initializeAnimations: -> + if @level.get('type', true) is 'hero' + @updateXPBars 0 @$el.find('#victory-header').delay(250).queue(-> $(@).removeClass('out').dequeue() Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'victory-title-appear' # TODO: actually add this @@ -166,14 +243,13 @@ module.exports = class HeroVictoryModal extends ModalView panel.queue(-> complete()) @animationComplete = not @animatedPanels.length complete() if @animationComplete - if @level.get('type', true) is 'hero-ladder' - @ladderSubmissionView = new LadderSubmissionView session: @session, level: @level - @insertSubView @ladderSubmissionView, @$el.find('.ladder-submission-view') beginSequentialAnimations: -> return if @destroyed + return unless @level.get('type', true) is 'hero' @sequentialAnimatedPanels = _.map(@animatedPanels.find('.reward-panel'), (panel) -> { number: $(panel).data('number') + previousNumber: $(panel).data('previous-number') textEl: $(panel).find('.reward-text') rootEl: $(panel) unit: $(panel).data('number-unit') @@ -200,26 +276,31 @@ module.exports = class HeroVictoryModal extends ModalView duration = 1000 ratio = @getEaseRatio (new Date() - @sequentialAnimationStart), duration if panel.unit is 'xp' - newXP = Math.floor(ratio * panel.number) + newXP = Math.floor(panel.previousNumber + ratio * (panel.number - panel.previousNumber)) totalXP = @totalXPAnimated + newXP if totalXP isnt @lastTotalXP panel.textEl.text('+' + newXP) - @XPEl.text('+' + totalXP) + @XPEl.text(totalXP) + @updateXPBars(totalXP) xpTrigger = 'xp-' + (totalXP % 6) # 6 xp sounds Backbone.Mediator.publish 'audio-player:play-sound', trigger: xpTrigger, volume: 0.5 + ratio / 2 + @XPEl.addClass 'four-digits' if totalXP >= 1000 and @lastTotalXP < 1000 + @XPEl.addClass 'five-digits' if totalXP >= 10000 and @lastTotalXP < 10000 @lastTotalXP = totalXP else if panel.unit is 'gem' - newGems = Math.floor(ratio * panel.number) + newGems = Math.floor(panel.previousNumber + ratio * (panel.number - panel.previousNumber)) totalGems = @totalGemsAnimated + newGems if totalGems isnt @lastTotalGems panel.textEl.text('+' + newGems) - @gemEl.text('+' + totalGems) + @gemEl.text(totalGems) gemTrigger = 'gem-' + (parseInt(panel.number * ratio) % 4) # 4 gem sounds Backbone.Mediator.publish 'audio-player:play-sound', trigger: gemTrigger, volume: 0.5 + ratio / 2 + @gemEl.addClass 'four-digits' if totalGems >= 1000 and @lastTotalGems < 1000 + @gemEl.addClass 'five-digits' if totalGems >= 10000 and @lastTotalGems < 10000 @lastTotalGems = totalGems else if panel.item thangType = @thangTypes[panel.item] - panel.textEl.text(thangType.get('name')) + panel.textEl.text utils.i18n(thangType.attributes, 'name') Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'item-unlocked', volume: 1 if 0.5 < ratio < 0.6 else if panel.hero thangType = @thangTypes[panel.hero] @@ -245,6 +326,37 @@ module.exports = class HeroVictoryModal extends ModalView --t -0.5 * (t * (t - 2) - 1) + updateXPBars: (achievedXP) -> + previousXP = @previousXP + previousXP = previousXP + 1000000 if me.isInGodMode() + previousLevel = @previousLevel + + currentXP = previousXP + achievedXP + currentLevel = User.levelFromExp currentXP + currentLevelXP = User.expForLevel currentLevel + + nextLevel = currentLevel + 1 + nextLevelXP = User.expForLevel nextLevel + + leveledUp = currentLevel > previousLevel + totalXPNeeded = nextLevelXP - currentLevelXP + alreadyAchievedPercentage = 100 * (previousXP - currentLevelXP) / totalXPNeeded + alreadyAchievedPercentage = 0 if alreadyAchievedPercentage < 0 # In case of level up + if leveledUp + newlyAchievedPercentage = 100 * (currentXP - currentLevelXP) / totalXPNeeded + else + newlyAchievedPercentage = 100 * achievedXP / totalXPNeeded + + xpEl = $('#xp-wrapper') + xpBarJustEarned = xpEl.find('.xp-bar-already-achieved').css('width', alreadyAchievedPercentage + '%') + xpBarTotal = xpEl.find('.xp-bar-total').css('width', (alreadyAchievedPercentage + newlyAchievedPercentage) + '%') + levelLabel = xpEl.find('.level') + utils.replaceText levelLabel, currentLevel + + if leveledUp and (not @displayedLevel or currentLevel > @displayedLevel) + @playSound 'level-up' + @displayedLevel = currentLevel + endSequentialAnimations: -> clearInterval @sequentialAnimationInterval @animationComplete = true @@ -252,7 +364,6 @@ module.exports = class HeroVictoryModal extends ModalView Backbone.Mediator.publish 'music-player:enter-menu', terrain: @level.get('terrain', true) updateSavingProgressStatus: -> - return unless @animationComplete @$el.find('#saving-progress-label').toggleClass('hide', @readyToContinue) @$el.find('.next-level-button').toggleClass('hide', not @readyToContinue) @$el.find('.sign-up-poke').toggleClass('hide', not @readyToContinue) @@ -260,7 +371,7 @@ module.exports = class HeroVictoryModal extends ModalView onGameSubmitted: (e) -> ladderURL = "/play/ladder/#{@level.get('slug')}#my-matches" # Preserve the supermodel as we navigate back to the ladder. - Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: require('views/play/ladder/LadderView'), viewArgs: [{supermodel: @supermodel}, @level.get('slug')] + Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: 'views/ladder/LadderView', viewArgs: [{supermodel: @supermodel}, @level.get('slug')] playSelectionSound: (hero, preload=false) -> return unless sounds = hero.get('soundTriggers')?.selected @@ -271,57 +382,93 @@ module.exports = class HeroVictoryModal extends ModalView else AudioPlayer.playSound name, 1 - getLevelInfoForSlug: (slug) -> - for campaign in require('views/play/WorldMapView').campaigns - for level in campaign.levels - return level if level.id is slug - getNextLevelCampaign: -> - # Wouldn't handle skipping/more practice across campaign boundaries, but we don't do that. - campaign = CampaignOptions.getCampaignForSlug @level.get 'slug' - if nextLevelSlug = @getNextLevel 'continue' - campaign = CampaignOptions.getCampaignForSlug nextLevelSlug - campaign or 'dungeon' + {'kithgard-gates': 'forest', 'kithgard-mastery': 'forest', 'siege-of-stonehold': 'desert', 'clash-of-clones': 'mountain'}[@level.get('slug')] or @level.get 'campaign' # Much easier to just keep this updated than to dynamically figure it out. - getNextLevelLink: (type) -> + getNextLevelLink: (returnToCourse=false) -> + if @level.get('type', true) is 'course' and nextLevel = @level.get('nextLevel') and not returnToCourse + # need to do something more complicated to load its slug + console.log 'have @nextLevel', @nextLevel, 'from nextLevel', nextLevel + return "/play/level/#{@nextLevel.get('slug')}" + else if @level.get('type', true) is 'course' + # TODO: figure out which course it is + return '/courses/mock1/0' link = '/play' nextCampaign = @getNextLevelCampaign() - link += '/' + nextCampaign unless nextCampaign is 'dungeon' - return link unless nextLevel = @getNextLevel type - "#{link}?next=#{nextLevel}" + link += '/' + nextCampaign + link - # Branching group testing - - getNextLevel: (type) -> - levelInfo = @getLevelInfoForSlug @level.get 'slug' - levelInfo?.nextLevels?[type] # 'more_practice', 'skip_ahead', 'continue' - - onClickContinue: (e) -> + onClickContinue: (e, extraOptions=null) -> @playSound 'menu-button-click' - nextLevelLink = @continueLevelLink - if me.getBranchingGroup() is 'all-practice' and @morePracticeLevelLink - nextLevelLink = @morePracticeLevelLink - skipPrompt = me.getBranchingGroup() in ['no-practice', 'all-practice'] - skipPrompt ||= not (@skipAheadLevelLink or @morePractiveLevelLink) and me.getBranchingGroup() is 'choice-explicit' - if skipPrompt - # Preserve the supermodel as we navigate back to the world map. - Backbone.Mediator.publish 'router:navigate', route: nextLevelLink, viewClass: require('views/play/WorldMapView'), viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @getNextLevelCampaign()] + nextLevelLink = @getNextLevelLink extraOptions?.returnToCourse + # Preserve the supermodel as we navigate back to the world map. + options = + justBeatLevel: @level + supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel + _.merge options, extraOptions if extraOptions + if @level.get('type', true) is 'course' and @nextLevel and not options.returnToCourse + viewClass = require 'views/play/level/PlayLevelView' + viewArgs = [options, @nextLevel.get('slug')] + else if @level.get('type', true) is 'course' + options.studentMode = true + viewClass = require 'views/courses/mock1/CourseDetailsView' + viewArgs = [options, '0'] else - # Hide everything except the buttons prompting them for which kind of next level to do - @$el.find('.modal-footer, .modal-body > *').hide() - @$el.find('.next-levels-prompt').show() + viewClass = require 'views/play/CampaignView' + viewArgs = [options, @getNextLevelCampaign()] + navigationEvent = route: nextLevelLink, viewClass: viewClass, viewArgs: viewArgs + if @level.get('slug') is 'lost-viking' and not (me.get('age') in ['0-13', '14-17']) + @showOffer navigationEvent + else + Backbone.Mediator.publish 'router:navigate', navigationEvent - onClickNextLevelBranch: (e) -> - @playSound 'menu-button-click' - e.preventDefault() - route = $(e.target).data('href') or "/play/#{@getNextLevelCampaign()}" - application.tracker?.trackEvent 'Branch Selected', level: @level.get('slug'), label: @level.get('slug'), branch: $(e.target).data('branch-key'), branchingGroup: me.getBranchingGroup(), route: route - # Preserve the supermodel as we navigate back to world map. - Backbone.Mediator.publish 'router:navigate', route: route, viewClass: require('views/play/WorldMapView'), viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @getNextLevelCampaign()] + onClickLeaderboard: (e) -> + @onClickContinue e, showLeaderboard: true + + onClickReturnToCourse: (e) -> + @onClickContinue e, returnToCourse: true onClickReturnToLadder: (e) -> @playSound 'menu-button-click' e.preventDefault() route = $(e.target).data('href') # Preserve the supermodel as we navigate back to the ladder. - Backbone.Mediator.publish 'router:navigate', route: route, viewClass: require('views/play/ladder/LadderView'), viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @level.get('slug')] + Backbone.Mediator.publish 'router:navigate', route: route, viewClass: 'views/ladder/LadderView', viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @level.get('slug')] + + onClickSignupButton: (e) -> + e.preventDefault() + window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Hero Victory Modal', level: @level.get('slug') + @openModalView new AuthModal {mode: 'signup'} + + showOffer: (@navigationEventUponCompletion) -> + @$el.find('.modal-footer > *').hide() + @$el.find(".modal-footer > .offer.#{@level.get('slug')}").show() + + onClickContinueFromOffer: (e) -> + url = { + 'lost-viking': 'http://www.vikingcodeschool.com/codecombat?utm_source=codecombat&utm_medium=viking_level&utm_campaign=affiliate&ref=Code+Combat+Elite' + }[@level.get('slug')] + Backbone.Mediator.publish 'router:navigate', @navigationEventUponCompletion + window.open url, '_blank' if url + + # Ratings and reviews + + starNum: (starEl) -> starEl.prevAll('i').length + 1 + + showStars: (num) -> + @$el.find('.rating').show() + num ?= @feedback?.get('rating') or 0 + stars = @$el.find('.rating i') + stars.removeClass('glyphicon-star').addClass('glyphicon-star-empty') + stars.slice(0, num).removeClass('glyphicon-star-empty').addClass('glyphicon-star') + + setStars: (num) -> + @feedback.set('rating', num) + @feedback.save() + + saveReviewEventually: -> + @saveReview() + + saveReview: -> + @feedback.set('review', @$el.find('.review textarea').val()) + @feedback.save() diff --git a/app/views/play/level/modal/InfiniteLoopModal.coffee b/app/views/play/level/modal/InfiniteLoopModal.coffee index 44d266d94..8ef2a5620 100644 --- a/app/views/play/level/modal/InfiniteLoopModal.coffee +++ b/app/views/play/level/modal/InfiniteLoopModal.coffee @@ -9,3 +9,8 @@ module.exports = class InfiniteLoopModal extends ModalView 'click #restart-level-infinite-loop-retry-button': -> Backbone.Mediator.publish 'tome:cast-spell', {} 'click #restart-level-infinite-loop-confirm-button': -> Backbone.Mediator.publish 'level:restart', {} 'click #restart-level-infinite-loop-comment-button': -> Backbone.Mediator.publish 'tome:comment-my-code', {} + + getRenderData: -> + c = super() + c.nonUserCodeProblem = @options.nonUserCodeProblem + c diff --git a/app/views/play/level/modal/VictoryModal.coffee b/app/views/play/level/modal/VictoryModal.coffee index 81ae265ef..51a0e34ab 100644 --- a/app/views/play/level/modal/VictoryModal.coffee +++ b/app/views/play/level/modal/VictoryModal.coffee @@ -1,4 +1,5 @@ ModalView = require 'views/core/ModalView' +AuthModal = require 'views/core/AuthModal' template = require 'templates/play/level/modal/victory' {me} = require 'core/auth' LadderSubmissionView = require 'views/play/common/LadderSubmissionView' @@ -13,8 +14,7 @@ module.exports = class VictoryModal extends ModalView 'ladder:game-submitted': 'onGameSubmitted' events: - 'click .next-level-button': 'onPlayNextLevel' - 'click .world-map-button': 'onClickWorldMap' + 'click .sign-up-button': 'onClickSignupButton' # review events 'mouseover .rating i': (e) -> @showStars(@starNum($(e.target))) @@ -24,9 +24,6 @@ module.exports = class VictoryModal extends ModalView @$el.find('.review').show() 'keypress .review textarea': -> @saveReviewEventually() - shortcuts: - 'enter': -> 'onPlayNextLevel' - constructor: (options) -> application.router.initializeSocialMediaServices() victory = options.level.get('victory', true) @@ -42,7 +39,7 @@ module.exports = class VictoryModal extends ModalView url = "/db/level/#{@level.id}/feedback" @feedback = new LevelFeedback() @feedback.setURL url - @feedback.fetch() + @feedback.fetch cache: false @listenToOnce(@feedback, 'sync', -> @onFeedbackLoaded()) @listenToOnce(@feedback, 'error', -> @onFeedbackNotFound()) @@ -59,14 +56,10 @@ module.exports = class VictoryModal extends ModalView @feedback.set('level', {majorVersion: @level.get('version').major, original: @level.get('original')}) @showStars() - onPlayNextLevel: -> - @saveReview() if @$el.find('.review textarea').val() - Backbone.Mediator.publish 'level:play-next-level', {} - - onClickWorldMap: (e) -> + onClickSignupButton: (e) -> e.preventDefault() - e.stopImmediatePropagation() - Backbone.Mediator.publish 'router:navigate', route: '/play', viewClass: require('views/play/WorldMapView'), viewArgs: [{supermodel: @supermodel}] + window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Victory Modal', level: @level.get('slug') + @openModalView new AuthModal {mode: 'signup'} onGameSubmitted: (e) -> ladderURL = "/play/ladder/#{@level.get('slug')}#my-matches" @@ -76,23 +69,10 @@ module.exports = class VictoryModal extends ModalView c = super() c.body = @body c.me = me - c.hasNextLevel = _.isObject(@level.get('nextLevel')) c.levelName = utils.i18n @level.attributes, 'name' c.level = @level - if c.level.get('type') in ['ladder', 'hero-ladder'] + if c.level.get('type') is 'ladder' c.readyToRank = @session.readyToRank() - if me.get 'hourOfCode' - # Show the Hour of Code "I'm Done" tracking pixel after they played for 30 minutes - elapsed = (new Date() - new Date(me.get('dateCreated'))) - enough = not c.hasNextLevel or elapsed >= 30 * 60 * 1000 - if enough and not me.get('hourOfCodeComplete') - $('body').append($('<img src="http://code.org/api/hour/finish_codecombat.png" style="visibility: hidden;">')) - me.set 'hourOfCodeComplete', true - me.patch() - window.tracker?.trackEvent 'Hour of Code Finish', {} - # Show the "I'm done" button if they get to the end, unless it's been over two hours - tooMuch = elapsed >= 120 * 60 * 1000 - c.showHourOfCodeDoneButton = not c.hasNextLevel and not tooMuch c afterRender: -> diff --git a/app/views/play/level/tome/CastButtonView.coffee b/app/views/play/level/tome/CastButtonView.coffee index 0d08f808b..ea114f6e2 100644 --- a/app/views/play/level/tome/CastButtonView.coffee +++ b/app/views/play/level/tome/CastButtonView.coffee @@ -1,7 +1,6 @@ CocoView = require 'views/core/CocoView' template = require 'templates/play/level/tome/cast_button' {me} = require 'core/auth' -LevelOptions = require 'lib/LevelOptions' module.exports = class CastButtonView extends CocoView id: 'cast-button-view' @@ -15,6 +14,7 @@ module.exports = class CastButtonView extends CocoView subscriptions: 'tome:spell-changed': 'onSpellChanged' 'tome:cast-spells': 'onCastSpells' + 'tome:manual-cast-denied': 'onManualCastDenied' 'god:new-world-created': 'onNewWorld' 'real-time-multiplayer:created-game': 'onJoinedRealTimeMultiplayerGame' 'real-time-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame' @@ -25,9 +25,13 @@ module.exports = class CastButtonView extends CocoView constructor: (options) -> super options @spells = options.spells - @levelID = options.levelID @castShortcut = '⇧↵' - @levelOptions = LevelOptions[@options.levelID] ? {} + @updateReplayabilityInterval = setInterval @updateReplayability, 1000 + @observing = options.session.get('creator') isnt me.id + + destroy: -> + clearInterval @updateReplayabilityInterval + super() getRenderData: (context={}) -> context = super context @@ -37,21 +41,20 @@ module.exports = class CastButtonView extends CocoView castRealTimeShortcutVerbose = (if @isMac() then 'Cmd' else 'Ctrl') + '+' + castShortcutVerbose context.castVerbose = castShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_code') context.castRealTimeVerbose = castRealTimeShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_real_time') + context.observing = @observing context afterRender: -> super() @castButton = $('.cast-button', @$el) - @castOptions = $('.autocast-delays', @$el) - #delay = me.get('autocastDelay') # No more autocast - delay = 90019001 - @setAutocastDelay delay - if @levelOptions.hidesSubmitUntilRun or @levelOptions.hidesRealTimePlayback + spell.view?.createOnCodeChangeHandlers() for spellKey, spell of @spells + if @options.level.get('hidesSubmitUntilRun') or @options.level.get 'hidesRealTimePlayback' @$el.find('.submit-button').hide() # Hide Submit for the first few until they run it once. - if @options.session.get('state')?.complete and @levelOptions.hidesRealTimePlayback + if @options.session.get('state')?.complete and @options.level.get 'hidesRealTimePlayback' @$el.find('.done-button').show() - if @options.levelID is 'thornbush-farm'# and not @options.session.get('state')?.complete + if @options.level.get('slug') is 'thornbush-farm'# and not @options.session.get('state')?.complete @$el.find('.submit-button').hide() # Hide submit until first win so that script can explain it. + @updateReplayability() attachTo: (spellView) -> @$el.detach().prependTo(spellView.toolbarView.$el).show() @@ -62,10 +65,15 @@ module.exports = class CastButtonView extends CocoView onCastRealTimeButtonClick: (e) -> if @inRealTimeMultiplayerSession Backbone.Mediator.publish 'real-time-multiplayer:manual-cast', {} + else if @options.level.get('replayable') and (timeUntilResubmit = @options.session.timeUntilResubmit()) > 0 + Backbone.Mediator.publish 'tome:manual-cast-denied', timeUntilResubmit: timeUntilResubmit else Backbone.Mediator.publish 'tome:manual-cast', {realTime: true} + @updateReplayability() onDoneButtonClick: (e) -> + return if @options.level.hasLocalChanges() # Don't award achievements when beating level changed in level editor + @options.session.recordScores @world.scores, @options.level Backbone.Mediator.publish 'level:show-victory', showModal: true onSpellChanged: (e) -> @@ -75,16 +83,22 @@ module.exports = class CastButtonView extends CocoView return if e.preload @casting = true if @hasStartedCastingOnce # Don't play this sound the first time - Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'cast', volume: 0.5 + @playSound 'cast', 0.5 @hasStartedCastingOnce = true @updateCastButton() + onManualCastDenied: (e) -> + wait = moment().add(e.timeUntilResubmit, 'ms').fromNow() + #@playSound 'manual-cast-denied', 1.0 # find some sound for this? + noty text: "You can try again #{wait}.", layout: 'center', type: 'warning', killer: false, timeout: 6000 + onNewWorld: (e) -> @casting = false if @hasCastOnce # Don't play this sound the first time - Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'cast-end', volume: 0.5 + @playSound 'cast-end', 0.5 @hasCastOnce = true @updateCastButton() + @world = e.world onNewGoalStates: (e) -> winnable = e.overallStatus is 'success' @@ -92,16 +106,16 @@ module.exports = class CastButtonView extends CocoView @winnable = winnable @$el.toggleClass 'winnable', @winnable Backbone.Mediator.publish 'tome:winnability-updated', winnable: @winnable - if @levelOptions.hidesRealTimePlayback + if @options.level.get 'hidesRealTimePlayback' @$el.find('.done-button').toggle @winnable - else if @winnable and @options.levelID is 'thornbush-farm' + else if @winnable and @options.level.get('slug') is 'thornbush-farm' @$el.find('.submit-button').show() # Hide submit until first win so that script can explain it. onGoalsCalculated: (e) -> # When preloading, with real-time playback enabled, we highlight the submit button when we think they'll win. return unless e.preload - return if @levelOptions.hidesRealTimePlayback - return if @options.levelID is 'thornbush-farm' # Don't show it until they actually win for this first one. + return if @options.level.get 'hidesRealTimePlayback' + return if @options.level.get('slug') is 'thornbush-farm' # Don't show it until they actually win for this first one. @onNewGoalStates e updateCastButton: -> @@ -116,23 +130,23 @@ module.exports = class CastButtonView extends CocoView castText = $.i18n.t('play_level.tome_cast_button_running') else if castable or true castText = $.i18n.t('play_level.tome_cast_button_run') - unless @levelOptions.hidesRunShortcut # Hide for first few. + unless @options.level.get 'hidesRunShortcut' # Hide for first few. castText += ' ' + @castShortcut else castText = $.i18n.t('play_level.tome_cast_button_ran') @castButton.text castText #@castButton.prop 'disabled', not castable - setAutocastDelay: (delay) -> - #console.log 'Set autocast delay to', delay - return unless delay - delay = 90019001 # No more autocast - @autocastDelay = delay = parseInt delay - me.set('autocastDelay', delay) - me.patch() - spell.view?.setAutocastDelay delay for spellKey, spell of @spells - @castOptions.find('a').each -> - $(@).toggleClass('selected', parseInt($(@).attr('data-delay')) is delay) + updateReplayability: => + return if @destroyed + return unless @options.level.get 'replayable' + timeUntilResubmit = @options.session.timeUntilResubmit() + disabled = timeUntilResubmit > 0 + submitButton = @$el.find('.submit-button').toggleClass('disabled', disabled) + submitAgainLabel = submitButton.find('.submit-again-time').toggleClass('secret', not disabled) + if disabled + waitTime = moment().add(timeUntilResubmit, 'ms').fromNow() + submitAgainLabel.text waitTime onJoinedRealTimeMultiplayerGame: (e) -> @inRealTimeMultiplayerSession = true diff --git a/app/views/play/level/tome/DocFormatter.coffee b/app/views/play/level/tome/DocFormatter.coffee index a4dc1a835..bc2772db7 100644 --- a/app/views/play/level/tome/DocFormatter.coffee +++ b/app/views/play/level/tome/DocFormatter.coffee @@ -38,7 +38,7 @@ safeJSONStringify = (input, maxDepth) -> module.exports = class DocFormatter constructor: (@options) -> - @doc = _.cloneDeep options.doc + @doc = _.cloneDeep @options.doc @fillOutDoc() fillOutDoc: -> @@ -55,16 +55,17 @@ module.exports = class DocFormatter when 'coffeescript' then '@' else 'this' if @doc.type is 'function' + [docName, args] = @getDocNameAndArguments() sep = {clojure: ' '}[@options.language] ? ', ' - argNames = (arg.name for arg in @doc.args ? []).join sep + argNames = args.join sep argString = if argNames then '__ARGS__' else '' @doc.shortName = switch @options.language - when 'coffeescript' then "#{ownerName}#{if ownerName is '@' then '' else '.'}#{@doc.name}#{if argString then ' ' + argString else '()'}" - when 'python' then "#{ownerName}.#{@doc.name}(#{argString})" - when 'lua' then "#{ownerName}:#{@doc.name}(#{argString})" - when 'clojure' then "(.#{@doc.name} #{ownerName}#{if argNames then ' ' + argString else ''})" - when 'io' then "#{if ownerName is 'this' then '' else ownerName + ' '}#{@doc.name}#{if argNames then '(' + argNames + ')' else ''}" - else "#{ownerName}.#{@doc.name}(#{argString});" + when 'coffeescript' then "#{ownerName}#{if ownerName is '@' then '' else '.'}#{docName}#{if argString then ' ' + argString else '()'}" + when 'python' then "#{ownerName}.#{docName}(#{argString})" + when 'lua' then "#{ownerName}:#{docName}(#{argString})" + when 'clojure' then "(.#{docName} #{ownerName}#{if argNames then ' ' + argString else ''})" + when 'io' then "#{if ownerName is 'this' then '' else ownerName + ' '}#{docName}#{if argNames then '(' + argNames + ')' else ''}" + else "#{ownerName}.#{docName}(#{argString});" else @doc.shortName = switch @options.language when 'coffeescript' then "#{ownerName}#{if ownerName is '@' then '' else '.'}#{@doc.name}" @@ -76,11 +77,13 @@ module.exports = class DocFormatter @doc.shorterName = @doc.shortName if @doc.type is 'function' and argString @doc.shortName = @doc.shorterName.replace argString, argNames - @doc.shorterName = @doc.shorterName.replace argString, (if argNames.length > 6 then '...' else argNames) + @doc.shorterName = @doc.shorterName.replace argString, (if not /cast[A-Z]/.test(@doc.name) and argNames.length > 6 then '...' else argNames) if @options.language is 'javascript' @doc.shorterName = @doc.shortName.replace ';', '' if @doc.owner is 'this' or @options.tabbify @doc.shorterName = @doc.shorterName.replace /^this\./, '' + else if (@options.language in ['python', 'lua']) and (@doc.owner is 'this' or @options.tabbify) + @doc.shorterName = @doc.shortName.replace /^self[:.]/, '' @doc.title = if @options.shortenize then @doc.shorterName else @doc.shortName # Grab the language-specific documentation for some sub-properties, if we have it. @@ -119,14 +122,28 @@ module.exports = class DocFormatter obj[prop] = @replaceSpriteName obj[prop] # Do this before using the template, otherwise marked might get us first. formatPopover: -> - content = popoverTemplate doc: @doc, language: @options.language, value: @formatValue(), marked: marked, argumentExamples: (arg.example or arg.default or arg.name for arg in @doc.args ? []), writable: @options.writable, selectedMethod: @options.selectedMethod, cooldowns: @inferCooldowns(), item: @options.item + [docName, args] = @getDocNameAndArguments() + argumentExamples = (arg.example or arg.default or arg.name for arg in @doc.args ? []) + argumentExamples.unshift args[0] if args.length > argumentExamples.length + content = popoverTemplate doc: @doc, docName: docName, language: @options.language, value: @formatValue(), marked: marked, argumentExamples: argumentExamples, writable: @options.writable, selectedMethod: @options.selectedMethod, cooldowns: @inferCooldowns(), item: @options.item owner = if @doc.owner is 'this' then @options.thang else window[@doc.owner] content = @replaceSpriteName content content.replace /\#\{(.*?)\}/g, (s, properties) => @formatValue downTheChain(owner, properties.split('.')) replaceSpriteName: (s) -> # Prefer type, and excluded the quotes we'd get with @formatValue - s.replace /#{spriteName}/g, @options.thang.type ? @options.thang.spriteName + name = @options.thang.type ? @options.thang.spriteName + name = 'hero' if /Hero Placeholder/.test @options.thang.id + s.replace /#{spriteName}/g, name + + getDocNameAndArguments: -> + return [@doc.name, []] unless @doc.type is 'function' + docName = @doc.name + args = (arg.name for arg in @doc.args ? []) + if /cast[A-Z]/.test docName + docName = 'cast' + args.unshift '"' + _.string.dasherize(@doc.name).replace('cast-', '') + '"' + [docName, args] formatValue: (v) -> return null if @doc.type is 'snippet' diff --git a/app/views/play/level/tome/ProblemAlertView.coffee b/app/views/play/level/tome/ProblemAlertView.coffee index 57ce6d064..942319568 100644 --- a/app/views/play/level/tome/ProblemAlertView.coffee +++ b/app/views/play/level/tome/ProblemAlertView.coffee @@ -1,4 +1,5 @@ CocoView = require 'views/core/CocoView' +GameMenuModal = require 'views/play/menu/GameMenuModal' template = require 'templates/play/level/tome/problem_alert' {me} = require 'core/auth' @@ -17,9 +18,13 @@ module.exports = class ProblemAlertView extends CocoView events: 'click .close': 'onRemoveClicked' + 'click #problem-alert-help-button': 'onClickProblemAlertHelp' constructor: (options) -> super options + @level = options.level + @session = options.session + @supermodel = options.supermodel if options.problem? @problem = options.problem @onWindowResize() @@ -69,6 +74,7 @@ module.exports = class ProblemAlertView extends CocoView @onWindowResize() @render() @onJiggleProblemAlert() + application.tracker?.trackEvent 'Show problem alert', {levelID: @level.get('slug'), ls: @session?.get('_id')} onJiggleProblemAlert: -> return unless @problem? @@ -80,8 +86,13 @@ module.exports = class ProblemAlertView extends CocoView _.delay pauseJiggle, 1000 onHideProblemAlert: -> + return unless @$el.is(':visible') @onRemoveClicked() + onClickProblemAlertHelp: -> + application.tracker?.trackEvent 'Problem alert help clicked', {levelID: @level.get('slug'), ls: @session?.get('_id')} + @openModalView new GameMenuModal showTab: 'guide', level: @level, session: @session, supermodel: @supermodel + onRemoveClicked: -> @playSound 'menu-button-click' @$el.hide() diff --git a/app/views/play/level/tome/Spell.coffee b/app/views/play/level/tome/Spell.coffee index 6d3683e2b..7e08eafa8 100644 --- a/app/views/play/level/tome/Spell.coffee +++ b/app/views/play/level/tome/Spell.coffee @@ -15,6 +15,7 @@ module.exports = class Spell @otherSession = options.otherSession @spectateView = options.spectateView @spectateOpponentCodeLanguage = options.spectateOpponentCodeLanguage + @observing = options.observing @supermodel = options.supermodel @skipProtectAPI = options.skipProtectAPI @worker = options.worker @@ -61,7 +62,7 @@ module.exports = class Spell setLanguage: (@language) -> #console.log 'setting language to', @language, 'so using original source', @languages[language] ? @languages.javascript - @originalSource = @languages[language] ? @languages.javascript + @originalSource = @languages[@language] ? @languages.javascript # Translate comments chosen spoken language. return unless @commentContext context = $.extend true, {}, @commentContext @@ -147,11 +148,11 @@ module.exports = class Spell cb(aether.hasChangedSignificantly((newSource ? @originalSource), (currentSource ? @source), true, true)) createAether: (thang) -> - aceConfig = me.get('aceConfig') ? {} writable = @permissions.readwrite.length > 0 skipProtectAPI = @skipProtectAPI or not writable problemContext = @createProblemContext thang - aetherOptions = createAetherOptions functionName: @name, codeLanguage: @language, functionParameters: @parameters, skipProtectAPI: skipProtectAPI, includeFlow: @levelType in ['hero', 'hero-ladder', 'hero-coop'], problemContext: problemContext + includeFlow = (@levelType in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder']) and not skipProtectAPI + aetherOptions = createAetherOptions functionName: @name, codeLanguage: @language, functionParameters: @parameters, skipProtectAPI: skipProtectAPI, includeFlow: includeFlow, problemContext: problemContext aether = new Aether aetherOptions if @worker workerMessage = @@ -188,6 +189,7 @@ module.exports = class Spell return true if @spectateView # Use transpiled code for both teams if we're just spectating. return true if @isEnemySpell() # Use transpiled for enemy spells. # Players without permissions can't view the raw code. + return false if @observing and @levelType in ['hero', 'course'] return true if @session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions', true)) false @@ -203,7 +205,7 @@ module.exports = class Spell @problemContext = { stringReferences: [], thisMethods: [], thisProperties: [] } # TODO: These should be read from the database - @problemContext.commonThisMethods = ['moveRight', 'moveLeft', 'moveUp', 'moveDown', 'attack', 'findNearestEnemy', 'buildXY', 'moveXY', 'say', 'move', 'distance', 'findEnemies', 'getFriends', 'addFlag', 'getFlag', 'removeFlag', 'getFlags', 'attackRange', 'cast', 'buildTypes', 'jump', 'jumpTo', 'attackXY'] + @problemContext.commonThisMethods = ['moveRight', 'moveLeft', 'moveUp', 'moveDown', 'attack', 'findNearestEnemy', 'buildXY', 'moveXY', 'say', 'move', 'distance', 'findEnemies', 'findFriends', 'addFlag', 'findFlag', 'removeFlag', 'findFlags', 'attackRange', 'cast', 'buildTypes', 'jump', 'jumpTo', 'attackXY'] return @problemContext unless thang? # Populate stringReferences diff --git a/app/views/play/level/tome/SpellListEntryView.coffee b/app/views/play/level/tome/SpellListEntryView.coffee index 6a17e1b9f..4bd68e71e 100644 --- a/app/views/play/level/tome/SpellListEntryView.coffee +++ b/app/views/play/level/tome/SpellListEntryView.coffee @@ -37,7 +37,7 @@ module.exports = class SpellListEntryView extends CocoView context createMethodSignature: -> - return @spell.name if @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] + return @spell.name if @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] parameters = (@spell.parameters or []).slice() if @spell.language in ['python', 'lua'] parameters.unshift 'self' diff --git a/app/views/play/level/tome/SpellListTabEntryView.coffee b/app/views/play/level/tome/SpellListTabEntryView.coffee index 3a2b9945e..65113cd1e 100644 --- a/app/views/play/level/tome/SpellListTabEntryView.coffee +++ b/app/views/play/level/tome/SpellListTabEntryView.coffee @@ -154,7 +154,7 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView break $codearea = $('#code-area') $codearea.on transitionListener, => - $codearea.css 'z-index', 1 unless $('html').hasClass 'fullscreen-editor' + $codearea.css 'z-index', 2 unless $('html').hasClass 'fullscreen-editor' destroy: -> diff --git a/app/views/play/level/tome/SpellPaletteEntryView.coffee b/app/views/play/level/tome/SpellPaletteEntryView.coffee index 22882e132..7c532a54e 100644 --- a/app/views/play/level/tome/SpellPaletteEntryView.coffee +++ b/app/views/play/level/tome/SpellPaletteEntryView.coffee @@ -107,8 +107,9 @@ module.exports = class SpellPaletteEntryView extends CocoView Backbone.Mediator.publish 'tome:palette-pin-toggled', entry: @, pinned: @popoverPinned onClick: (e) => - if @options.level.get('type', true) is 'hero' + if true or @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] # Jiggle instead of pin for hero levels + # Actually, do it all the time, because we recently busted the pin CSS. TODO: restore pinning jigglyPopover = $('.spell-palette-popover.popover') jigglyPopover.addClass 'jiggling' pauseJiggle = => diff --git a/app/views/play/level/tome/SpellPaletteView.coffee b/app/views/play/level/tome/SpellPaletteView.coffee index 0317e6a33..5e0afd74b 100644 --- a/app/views/play/level/tome/SpellPaletteView.coffee +++ b/app/views/play/level/tome/SpellPaletteView.coffee @@ -5,7 +5,7 @@ filters = require 'lib/image_filter' SpellPaletteEntryView = require './SpellPaletteEntryView' LevelComponent = require 'models/LevelComponent' ThangType = require 'models/ThangType' -LevelOptions = require 'lib/LevelOptions' +GameMenuModal = require 'views/play/menu/GameMenuModal' N_ROWS = 4 @@ -20,9 +20,17 @@ module.exports = class SpellPaletteView extends CocoView 'surface:frame-changed': 'onFrameChanged' 'tome:change-language': 'onTomeChangedLanguage' + events: + 'click #spell-palette-help-button': 'onClickHelp' + constructor: (options) -> super options + @level = options.level + @session = options.session + @supermodel = options.supermodel @thang = options.thang + docs = @options.level.get('documentation') ? {} + @showsHelp = docs.specificArticles?.length or docs.generalArticles?.length @createPalette() $(window).on 'resize', @onResize @@ -33,6 +41,10 @@ module.exports = class SpellPaletteView extends CocoView c.entryGroupNames = @entryGroupNames c.tabbed = _.size(@entryGroups) > 1 c.defaultGroupSlug = @defaultGroupSlug + c.showsHelp = @showsHelp + c.tabs = @tabs # For hero-based, non-this-owned tabs like Vector, Math, etc. + c.thisName = {coffeescript: '@', lua: 'self', python: 'self'}[@options.language] or 'this' + c._ = _ c afterRender: -> @@ -50,7 +62,7 @@ module.exports = class SpellPaletteView extends CocoView else @entryGroupElements = {} for group, entries of @entryGroups - @entryGroupElements[group] = itemGroup = $('<div class="property-entry-item-group"></div>').appendTo @$el.find('.properties') + @entryGroupElements[group] = itemGroup = $('<div class="property-entry-item-group"></div>').appendTo @$el.find('.properties-this') if entries[0].options.item?.getPortraitURL itemImage = $('<img class="item-image" draggable=false></img>').attr('src', entries[0].options.item.getPortraitURL()).css('top', Math.max(0, 19 * (entries.length - 2) / 2) + 2) itemGroup.append itemImage @@ -65,7 +77,20 @@ module.exports = class SpellPaletteView extends CocoView entry.$el.addClass 'single-entry' if entryIndex is 0 entry.$el.addClass 'first-entry' + for tab, entries of @tabs or {} + tabSlug = _.string.slugify tab + itemsInGroup = 0 + for entry, entryIndex in entries + if itemsInGroup is 0 or (itemsInGroup is 2 and entryIndex isnt entries.length - 1) + itemGroup = $('<div class="property-entry-item-group"></div>').appendTo @$el.find(".properties-#{tabSlug}") + itemsInGroup = 0 + ++itemsInGroup + itemGroup.append entry.el + entry.render() # Render after appending so that we can access parent container for popover + if itemsInGroup is 0 + entry.$el.addClass 'first-entry' @$el.addClass 'hero' + @$el.toggleClass 'shortenize', Boolean @shortenize @updateMaxHeight() unless application.isIPadApp afterInsert: -> @@ -77,18 +102,27 @@ module.exports = class SpellPaletteView extends CocoView updateMaxHeight: -> return unless @isHero - nColumns = Math.floor @$el.find('.properties').innerWidth() / 212 # ~212px is a good max entry width; will always have 2 columns + # We figure out how many columns we can fit, width-wise, and then guess how many rows will be needed. + # We can then assign a height based on the number of rows, and the flex layout will do the rest. + columnWidth = if @shortenize then 175 else 212 + nColumns = Math.floor @$el.find('.properties-this').innerWidth() / columnWidth # will always have 2 columns, since at 1024px screen we have 424px .properties columns = ({items: [], nEntries: 0} for i in [0 ... nColumns]) + orderedColumns = [] nRows = 0 - for group, entries of @entryGroups + entryGroupsByLength = _.sortBy _.keys(@entryGroups), (group) => @entryGroups[group].length + entryGroupsByLength.reverse() + for group in entryGroupsByLength + entries = @entryGroups[group] continue unless shortestColumn = _.sortBy(columns, (column) -> column.nEntries)[0] - shortestColumn.nEntries += Math.max 2, entries.length + shortestColumn.nEntries += Math.max 2, entries.length # Item portrait is two rows tall shortestColumn.items.push @entryGroupElements[group] + orderedColumns.push shortestColumn unless shortestColumn in orderedColumns nRows = Math.max nRows, shortestColumn.nEntries - for column in columns + for column in orderedColumns for item in column.items - item.detach().appendTo @$el.find('.properties') - @$el.find('.properties').css('height', 19 * (nRows + 1)) + item.detach().appendTo @$el.find('.properties-this') + desiredHeight = 19 * (nRows + 1) + @$el.find('.properties').css('height', desiredHeight) onResize: (e) => @updateMaxHeight() @@ -127,7 +161,7 @@ module.exports = class SpellPaletteView extends CocoView else propStorage = 'this': ['apiProperties', 'apiMethods'] - if not (@options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop']) or not @options.programmable + if not (@options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder']) or not @options.programmable @organizePalette propStorage, allDocs, excludedDocs else @organizePaletteHero propStorage, allDocs, excludedDocs @@ -145,7 +179,7 @@ module.exports = class SpellPaletteView extends CocoView count += added.length Backbone.Mediator.publish 'tome:update-snippets', propGroups: propGroups, allDocs: allDocs, language: @options.language - shortenize = count > 6 + @shortenize = count > 6 tabbify = count >= 10 @entries = [] for owner, props of propGroups @@ -157,7 +191,7 @@ module.exports = class SpellPaletteView extends CocoView console.log 'could not find doc for', prop, 'from', allDocs['__' + prop], 'for', owner, 'of', propGroups doc ?= prop if doc - @entries.push @addEntry(doc, shortenize, tabbify, owner is 'snippets') + @entries.push @addEntry(doc, @shortenize, owner is 'snippets') groupForEntry = (entry) -> return 'more' if entry.doc.owner is 'this' and entry.doc.name in (propGroups.more ? []) entry.doc.owner @@ -169,7 +203,7 @@ module.exports = class SpellPaletteView extends CocoView if tabbify and _.find @entries, ((entry) -> entry.doc.owner isnt 'this') @entryGroups = _.groupBy @entries, groupForEntry else - i18nKey = if @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] then 'play_level.tome_your_skills' else 'play_level.tome_available_spells' + i18nKey = if @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] then 'play_level.tome_your_skills' else 'play_level.tome_available_spells' defaultGroup = $.i18n.t i18nKey @entryGroups = {} @entryGroups[defaultGroup] = @entries @@ -180,7 +214,7 @@ module.exports = class SpellPaletteView extends CocoView @entryGroups[group] = _.groupBy entries, (entry, i) -> Math.floor i / N_ROWS @entryGroupSlugs[group] = _.string.slugify group @entryGroupNames[group] = group - if thisName = {coffeescript: '@', lua: 'self', clojure: 'self'}[@options.language] + if thisName = {coffeescript: '@', lua: 'self', python: 'self'}[@options.language] if @entryGroupNames.this @entryGroupNames.this = thisName @@ -204,6 +238,7 @@ module.exports = class SpellPaletteView extends CocoView for owner, storages of propStorage if props = component.config[storages] for prop in _.sortBy(props) when prop[0] isnt '_' and not itemsByProp[prop] # no private properties + continue if prop is 'moveXY' and @options.level.get('slug') is 'slalom' # Hide for Slalom propsByItem[item.get('name')] ?= [] propsByItem[item.get('name')].push owner: owner, prop: prop, item: item itemsByProp[prop] = item @@ -211,18 +246,32 @@ module.exports = class SpellPaletteView extends CocoView else console.log @thang.id, "couldn't find item ThangType for", slot, thangTypeName + # Get any Math-, Vector-, etc.-owned properties into their own tabs + for owner, storage of propStorage when not (owner in ['this', 'more', 'snippets']) + continue unless @thang[storage]?.length + @tabs ?= {} + @tabs[owner] = [] + programmaticonName = @thang.inventoryThangTypeNames['programming-book'] + programmaticon = itemThangTypes[programmaticonName] + sortedProps = @thang[storage].slice().sort() + for prop in sortedProps + if doc = _.find (allDocs['__' + prop] ? []), {owner: owner} # Not all languages have all props + entry = @addEntry doc, false, false, programmaticon + @tabs[owner].push entry + # Assign any unassigned properties to the hero itself. for owner, storage of propStorage + continue unless owner in ['this', 'more', 'snippets'] for prop in _.reject(@thang[storage] ? [], (prop) -> itemsByProp[prop] or prop[0] is '_') # no private properties - if prop is 'say' and LevelOptions[@options.level.get('slug')]?.hidesSay # Hide for Dungeon Campaign - continue + continue if prop is 'say' and @options.level.get 'hidesSay' # Hide for Dungeon Campaign + continue if prop is 'moveXY' and @options.level.get('slug') is 'slalom' # Hide for Slalom propsByItem['Hero'] ?= [] propsByItem['Hero'].push owner: owner, prop: prop, item: itemThangTypes[@thang.spriteName] ++propCount Backbone.Mediator.publish 'tome:update-snippets', propGroups: propsByItem, allDocs: allDocs, language: @options.language - shortenize = propCount > 6 + @shortenize = propCount > 6 @entries = [] for itemName, props of propsByItem for prop, propIndex in props @@ -236,7 +285,7 @@ module.exports = class SpellPaletteView extends CocoView console.log 'could not find doc for', prop, 'from', allDocs['__' + prop], 'for', owner, 'of', propsByItem, 'with item', item doc ?= prop if doc - @entries.push @addEntry(doc, shortenize, false, owner is 'snippets', item, propIndex > 0) + @entries.push @addEntry(doc, @shortenize, owner is 'snippets', item, propIndex > 0) @entryGroups = _.groupBy @entries, (entry) -> itemsByProp[entry.doc.name]?.get('name') ? 'Hero' iOSEntryGroups = {} for group, entries of @entryGroups @@ -245,9 +294,9 @@ module.exports = class SpellPaletteView extends CocoView props: (entry.doc for entry in entries) Backbone.Mediator.publish 'tome:palette-updated', thangID: @thang.id, entryGroups: JSON.stringify(iOSEntryGroups) - addEntry: (doc, shortenize, tabbify, isSnippet=false, item=null, showImage=false) -> + addEntry: (doc, shortenize, isSnippet=false, item=null, showImage=false) -> writable = (if _.isString(doc) then doc else doc.name) in (@thang.apiUserProperties ? []) - new SpellPaletteEntryView doc: doc, thang: @thang, shortenize: shortenize, tabbify: tabbify, isSnippet: isSnippet, language: @options.language, writable: writable, level: @options.level, item: item, showImage: showImage + new SpellPaletteEntryView doc: doc, thang: @thang, shortenize: shortenize, isSnippet: isSnippet, language: @options.language, writable: writable, level: @options.level, item: item, showImage: showImage onDisableControls: (e) -> @toggleControls e, false onEnableControls: (e) -> @toggleControls e, true @@ -257,16 +306,6 @@ module.exports = class SpellPaletteView extends CocoView @controlsEnabled = enabled @$el.find('*').attr('disabled', not enabled) @$el.toggleClass 'controls-disabled', not enabled - @toggleBackground() - - toggleBackground: => - # TODO: make the palette background an actual background and do the CSS trick - # used in spell_list_entry.sass for disabling - background = @$el.find('img.code-palette-background')[0] - if background.naturalWidth is 0 # not loaded yet - return _.delay @toggleBackground, 100 - filters.revertImage background, 'span.code-palette-background' if @controlsEnabled - filters.darkenImage background, 'span.code-palette-background', 0.8 unless @controlsEnabled onFrameChanged: (e) -> return unless e.selectedThang?.id is @thang.id @@ -278,6 +317,10 @@ module.exports = class SpellPaletteView extends CocoView @createPalette() @render() + onClickHelp: (e) -> + application.tracker?.trackEvent 'Spell palette help clicked', levelID: @level.get('slug') + @openModalView new GameMenuModal showTab: 'guide', level: @level, session: @session, supermodel: @supermodel + destroy: -> entry.destroy() for entry in @entries @toggleBackground = null diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee index 7ac85ef66..7756622d7 100644 --- a/app/views/play/level/tome/SpellView.coffee +++ b/app/views/play/level/tome/SpellView.coffee @@ -9,8 +9,6 @@ SpellDebugView = require './SpellDebugView' SpellToolbarView = require './SpellToolbarView' LevelComponent = require 'models/LevelComponent' UserCodeProblem = require 'models/UserCodeProblem' -CampaignOptions = require 'lib/CampaignOptions' -LevelOptions = require 'lib/LevelOptions' module.exports = class SpellView extends CocoView id: 'spell-view' @@ -71,12 +69,15 @@ module.exports = class SpellView extends CocoView @writable = false unless me.team in @spell.permissions.readwrite # TODO: make this do anything @highlightCurrentLine = _.throttle @highlightCurrentLine, 100 $(window).on 'resize', @onWindowResize + @observing = @session.get('creator') isnt me.id afterRender: -> super() @createACE() @createACEShortcuts() @fillACE() + @createOnCodeChangeHandlers() + @lockDefaultCode() if @session.get('multiplayer') @createFirepad() else @@ -120,10 +121,15 @@ module.exports = class SpellView extends CocoView name: 'run-code' bindKey: {win: 'Shift-Enter|Ctrl-Enter', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter'} exec: -> Backbone.Mediator.publish 'tome:manual-cast', {} - addCommand - name: 'run-code-real-time' - bindKey: {win: 'Ctrl-Shift-Enter', mac: 'Command-Shift-Enter|Ctrl-Shift-Enter'} - exec: -> Backbone.Mediator.publish 'tome:manual-cast', {realTime: true} + unless @observing + addCommand + name: 'run-code-real-time' + bindKey: {win: 'Ctrl-Shift-Enter', mac: 'Command-Shift-Enter|Ctrl-Shift-Enter'} + exec: => + if @options.level.get('replayable') and (timeUntilResubmit = @session.timeUntilResubmit()) > 0 + Backbone.Mediator.publish 'tome:manual-cast-denied', timeUntilResubmit: timeUntilResubmit + else + Backbone.Mediator.publish 'tome:manual-cast', {realTime: true} addCommand name: 'no-op' bindKey: {win: 'Ctrl-S', mac: 'Command-S|Ctrl-S'} @@ -142,13 +148,11 @@ module.exports = class SpellView extends CocoView Backbone.Mediator.publish 'level:shift-space-pressed', {} else @ace.insert ' ' - addCommand name: 'end-all-scripts' bindKey: {win: 'Escape', mac: 'Escape'} readOnly: true exec: -> - console.log 'esc pressed' Backbone.Mediator.publish 'level:escape-pressed', {} addCommand name: 'toggle-grid' @@ -199,7 +203,7 @@ module.exports = class SpellView extends CocoView bindKey: {win: 'Ctrl-Shift-M', mac: 'Command-Shift-M|Ctrl-Shift-M'} exec: -> Backbone.Mediator.publish 'tome:toggle-maximize', {} addCommand - # TODO: Restrict to beginner campaign levels, possibly with a CampaignOptions similar to LevelOptions + # TODO: Restrict to beginner campaign levels like we do backspaceThrottle name: 'enter-skip-delimiters' bindKey: 'Enter|Return' exec: => @@ -215,37 +219,42 @@ module.exports = class SpellView extends CocoView addCommand name: 'disable-spaces' bindKey: 'Space' - exec: => @ace.execCommand 'insertstring', ' ' unless LevelOptions[@options.level.get('slug')]?.disableSpaces - addCommand - name: 'throttle-backspaces' - bindKey: 'Backspace' exec: => - # Throttle the backspace speed - # Slow to 500ms when whitespace at beginning of line is first encountered - # Slow to 100ms for remaining whitespace at beginning of line - # Rough testing showed backspaces happen at 150ms when tapping. - # Backspace speed varies by system when holding, 30ms on fastest Macbook setting. - unless CampaignOptions?.getOption?(@options?.level?.get?('slug'), 'backspaceThrottle') + disableSpaces = @options.level.get('disableSpaces') or false + aceConfig = me.get('aceConfig') ? {} + disableSpaces = false if aceConfig.keyBindings and aceConfig.keyBindings isnt 'default' # Not in vim/emacs mode + disableSpaces = false if @spell.language in ['clojure', 'lua', 'coffeescript', 'io'] # Don't disable for more advanced/experimental languages + if not disableSpaces or (_.isNumber(disableSpaces) and disableSpaces < me.level()) + return @ace.execCommand 'insertstring', ' ' + line = @aceDoc.getLine @ace.getCursorPosition().row + return @ace.execCommand 'insertstring', ' ' if @singleLineCommentRegex().test line + + if @options.level.get 'backspaceThrottle' + addCommand + name: 'throttle-backspaces' + bindKey: 'Backspace' + exec: => + # Throttle the backspace speed + # Slow to 500ms when whitespace at beginning of line is first encountered + # Slow to 100ms for remaining whitespace at beginning of line + # Rough testing showed backspaces happen at 150ms when tapping. + # Backspace speed varies by system when holding, 30ms on fastest Macbook setting. + nowDate = Date.now() + if @aceSession.selection.isEmpty() + cursor = @ace.getCursorPosition() + line = @aceDoc.getLine(cursor.row) + if /^\s*$/.test line.substring(0, cursor.column) + @backspaceThrottleMs ?= 500 + # console.log "SpellView @backspaceThrottleMs=#{@backspaceThrottleMs}" + # console.log 'SpellView lastBackspace diff', nowDate - @lastBackspace if @lastBackspace? + if not @lastBackspace? or nowDate - @lastBackspace > @backspaceThrottleMs + @backspaceThrottleMs = 100 + @lastBackspace = nowDate + @ace.remove "left" + return + @backspaceThrottleMs = null + @lastBackspace = nowDate @ace.remove "left" - return - - nowDate = Date.now() - if @aceSession.selection.isEmpty() - cursor = @ace.getCursorPosition() - line = @aceDoc.getLine(cursor.row) - if /^\s*$/.test line.substring(0, cursor.column) - @backspaceThrottleMs ?= 500 - # console.log "SpellView @backspaceThrottleMs=#{@backspaceThrottleMs}" - # console.log 'SpellView lastBackspace diff', nowDate - @lastBackspace if @lastBackspace? - if not @lastBackspace? or nowDate - @lastBackspace > @backspaceThrottleMs - @backspaceThrottleMs = 100 - @lastBackspace = nowDate - @ace.remove "left" - return - @backspaceThrottleMs = null - @lastBackspace = nowDate - @ace.remove "left" - fillACE: -> @@ -253,11 +262,131 @@ module.exports = class SpellView extends CocoView @aceSession.setUndoManager(new UndoManager()) @ace.clearSelection() + lockDefaultCode: (force=false) -> + # TODO: Lock default indent for an empty line? + lockDefaultCode = @options.level.get('lockDefaultCode') or false + if not lockDefaultCode or (_.isNumber(lockDefaultCode) and lockDefaultCode < me.level()) + return + return unless @spell.source is @spell.originalSource or force + return if @isIE() # Temporary workaround for #2512 + aceConfig = me.get('aceConfig') ? {} + return if aceConfig.keyBindings and aceConfig.keyBindings isnt 'default' # Don't lock in vim/emacs mode + + console.info 'Locking down default code.' + + intersects = => + return true for range in @readOnlyRanges when @ace.getSelectionRange().intersects(range) + false + + intersectsLeft = => + leftRange = @ace.getSelectionRange().clone() + if leftRange.start.column > 0 + leftRange.setStart leftRange.start.row, leftRange.start.column - 1 + else if leftRange.start.row > 0 + leftRange.setStart leftRange.start.row - 1, 0 + return true for range in @readOnlyRanges when leftRange.intersects(range) + false + + intersectsRight = => + rightRange = @ace.getSelectionRange().clone() + if rightRange.end.column < @aceDoc.getLine(rightRange.end.row).length + rightRange.setEnd rightRange.end.row, rightRange.end.column + 1 + else if rightRange.start.row < @aceDoc.getLength() - 1 + rightRange.setEnd rightRange.end.row + 1, 0 + return true for range in @readOnlyRanges when rightRange.intersects(range) + false + + pulseLockedCode = -> + $('.locked-code').finish().addClass('pulsating').effect('shake', times: 1, distance: 2, direction: 'down').removeClass('pulsating') + + preventReadonly = (next) -> + if intersects() + pulseLockedCode() + return true + next?() + + interceptCommand = (obj, method, wrapper) -> + orig = obj[method] + obj[method] = -> + args = Array.prototype.slice.call arguments + wrapper => orig.apply obj, args + obj[method] + + + finishRange = (row, startRow, startColumn) => + range = new Range startRow, startColumn, row, @aceSession.getLine(row).length - 1 + range.start = @aceDoc.createAnchor range.start + range.end = @aceDoc.createAnchor range.end + range.end.$insertRight = true + @readOnlyRanges.push range + + # Remove previous locked code highlighting + if @lockedCodeMarkerIDs? + @aceSession.removeMarker marker for marker in @lockedCodeMarkerIDs + @lockedCodeMarkerIDs = [] + + # Create locked default code text ranges + @readOnlyRanges = [] + if @spell.language in ['python', 'coffeescript'] + # Lock contiguous section of default code + # Only works for languages without closing delimeters on blocks currently + lines = @aceDoc.getAllLines() + for line, row in lines when not /^\s*$/.test(line) + lastRow = row + if lastRow? + @readOnlyRanges.push new Range 0, 0, lastRow, lines[lastRow].length - 1 + + # TODO: Highlighting does not work for multiple ranges + # TODO: Everything looks correct except the actual result. + # TODO: https://github.com/codecombat/codecombat/issues/1852 + # else + # # Create a read-only range for each chunk of text not separated by an empty line + # startRow = startColumn = null + # for row in [0...@aceSession.getLength()] + # unless /^\s*$/.test @aceSession.getLine(row) + # unless startRow? and startColumn? + # startRow = row + # startColumn = 0 + # else + # if startRow? and startColumn? + # finishRange row - 1, startRow, startColumn + # startRow = startColumn = null + # if startRow? and startColumn? + # finishRange @aceSession.getLength() - 1, startRow, startColumn + + # Highlight locked ranges + for range in @readOnlyRanges + @lockedCodeMarkerIDs.push @aceSession.addMarker range, 'locked-code', 'fullLine' + + # Override write operations that intersect with default code + interceptCommand @ace, 'onPaste', preventReadonly + interceptCommand @ace, 'onCut', preventReadonly + # TODO: can we use interceptCommand for this too? 'exec' and 'onExec' did not work. + @ace.commands.on 'exec', (e) => + e.stopPropagation() + e.preventDefault() + if (e.command.name is 'insertstring' and intersects()) or + (e.command.name in ['Backspace', 'throttle-backspaces'] and intersectsLeft()) or + (e.command.name is 'del' and intersectsRight()) + @zatanna?.off?() + pulseLockedCode() + return false + else if e.command.name in ['enter-skip-delimiters', 'Enter', 'Return'] + if intersects() + e.editor.navigateDown 1 + e.editor.navigateLineStart() + return false + else if e.command.name in ['Enter', 'Return'] and not e.editor?.completer?.popup?.isOpen + @zatanna?.on?() + return e.editor.execCommand 'enter-skip-delimiters' + @zatanna?.on?() + e.command.exec e.editor, e.args or {} + initAutocomplete: (@autocomplete) -> # TODO: Turn on more autocompletion based on level sophistication # TODO: E.g. using the language default snippets yields a bunch of crazy non-beginner suggestions # TODO: Options logic shouldn't exist both here and in updateAutocomplete() - popupFontSizePx = CampaignOptions.getOption(@options.level.get('slug'), 'autocompleteFontSizePx') ? 16 + popupFontSizePx = @options.level.get('autocompleteFontSizePx') ? 16 @zatanna = new Zatanna @ace, basic: false liveCompletion: false @@ -282,6 +411,8 @@ module.exports = class SpellView extends CocoView # tabTrigger: fallback for name field return unless @zatanna and @autocomplete snippetEntries = [] + haveFindNearestEnemy = false + haveFindNearest = false for group, props of e.propGroups for prop in props if _.isString prop # organizePalette @@ -294,7 +425,7 @@ module.exports = class SpellView extends CocoView return (owner is 'this' or owner is 'more') and (not doc.owner? or doc.owner is 'this') if doc?.snippets?[e.language] content = doc.snippets[e.language].code - if /loop/.test(content) and LevelOptions[@options.level.get('slug')]?.moveRightLoopSnippet + if /loop/.test(content) and @options.level.get 'moveRightLoopSnippet' # Replace default loop snippet with an embedded moveRight() content = switch e.language when 'python' then 'loop:\n self.moveRight()\n ${1:}' @@ -302,12 +433,11 @@ module.exports = class SpellView extends CocoView else content entry = content: content - meta: 'press tab' + meta: $.i18n.t('keyboard_shortcuts.press_enter', defaultValue: 'press enter') name: doc.name tabTrigger: doc.snippets[e.language].tab - if doc.name is 'findNearestEnemy' - # Remember if we have findNearestEnemy so attack snippet can be updated - haveFindNearestEnemy = true + haveFindNearestEnemy ||= doc.name is 'findNearestEnemy' + haveFindNearest ||= doc.name is 'findNearest' if doc.name is 'attack' # Postpone this until we know if findNearestEnemy is available attackEntry = entry @@ -317,15 +447,37 @@ module.exports = class SpellView extends CocoView # TODO: Generalize this snippet replacement # TODO: Where should this logic live, and what format should it be in? if attackEntry? - unless haveFindNearestEnemy + unless haveFindNearestEnemy or haveFindNearest or @options.level.get('slug') is 'known-enemy' # No findNearestEnemy, so update attack snippet to string-based target + # (On Known Enemy, we are introducing enemy2 = "Gert", so we want them to do attack(enemy2).) attackEntry.content = attackEntry.content.replace '${1:enemy}', '"${1:Enemy Name}"' snippetEntries.push attackEntry - # window.zatannaInstance = @zatanna + if haveFindNearest and not haveFindNearestEnemy + @translateFindNearest() + + # window.zatannaInstance = @zatanna # For debugging. Make sure to not leave active when committing. # window.snippetEntries = snippetEntries lang = SpellView.editModes[e.language].substr 'ace/mode/'.length @zatanna.addSnippets snippetEntries, lang + @editorLang = lang + + translateFindNearest: -> + # If they have advanced glasses but are playing a level which assumes earlier glasses, we'll adjust the sample code to use the more advanced APIs instead. + oldSource = @getSource() + if @spell.language is 'clojure' + newSource = oldSource.replace /\(.findNearestEnemy this\)/g, "(.findNearest this (.findEnemies this))" + newSource = newSource.replace /\(.findNearestItem this\)/g, "(.findNearest this (.findItems this))" + else if @spell.language is 'io' + newSource = oldSource.replace /findNearestEnemy/g, "findNearest(findEnemies)" + newSource = newSource.replace /findNearestItem/g, "findNearest(findItems)" + else + newSource = oldSource.replace /(self:|self.|this.|@)findNearestEnemy\(\)/g, "$1findNearest($1findEnemies())" + newSource = newSource.replace /(self:|self.|this.|@)findNearestItem\(\)/g, "$1findNearest($1findItems())" + return if oldSource is newSource + @spell.originalSource = newSource + @updateACEText newSource + _.delay (=> @recompile?()), 1000 onMultiplayerChanged: -> if @session.get('multiplayer') @@ -367,7 +519,7 @@ module.exports = class SpellView extends CocoView @createToolbarView() createDebugView: -> - return if @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] # We'll turn this on later, maybe, but not yet. + return if @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] # We'll turn this on later, maybe, but not yet. @debugView = new SpellDebugView ace: @ace, thang: @thang, spell:@spell @$el.append @debugView.render().$el.hide() @@ -424,15 +576,22 @@ module.exports = class SpellView extends CocoView @lastScreenLineCount = screenLineCount lineHeight = @ace.renderer.lineHeight or 20 tomeHeight = $('#tome-view').innerHeight() + spellPaletteView = $('#spell-palette-view') spellListTabEntryHeight = $('#spell-list-tab-entry-view').outerHeight() spellToolbarHeight = $('.spell-toolbar-view').outerHeight() - spellPaletteHeight = $('#spell-palette-view').outerHeight() - maxHeight = tomeHeight - spellListTabEntryHeight - spellToolbarHeight - spellPaletteHeight + @spellPaletteHeight ?= spellPaletteView.outerHeight() # Remember this until resize, since we change it afterward + spellPaletteAllowedHeight = Math.min @spellPaletteHeight, tomeHeight / 3 + maxHeight = tomeHeight - spellListTabEntryHeight - spellToolbarHeight - spellPaletteAllowedHeight linesAtMaxHeight = Math.floor(maxHeight / lineHeight) lines = Math.max 8, Math.min(screenLineCount + 2, linesAtMaxHeight) # 2 lines buffer is nice @ace.setOptions minLines: lines, maxLines: lines - $('#spell-palette-view').css('top', 175 + lineHeight * lines) # Move spell palette up, slightly overlapping us. + # Move spell palette up, slightly overlapping us. + newTop = 175 + lineHeight * lines + spellPaletteView.css('top', newTop) + # Expand it to bottom of tome if too short. + newHeight = Math.max @spellPaletteHeight, tomeHeight - newTop + 10 + spellPaletteView.css('height', newHeight) if @spellPaletteHeight isnt newHeight hideProblemAlert: -> Backbone.Mediator.publish 'tome:hide-problem-alert', {} @@ -450,14 +609,12 @@ module.exports = class SpellView extends CocoView reloadCode: (cast=true) -> @updateACEText @spell.originalSource + @lockDefaultCode true @recompile cast Backbone.Mediator.publish 'tome:spell-loaded', spell: @spell - - recompileIfNeeded: => - @recompile() if @recompileNeeded + @updateLines() recompile: (cast=true, realTime=false) -> - @setRecompileNeeded false hasChanged = @spell.source isnt @getSource() if hasChanged @spell.transpile @getSource() @@ -480,17 +637,9 @@ module.exports = class SpellView extends CocoView catch error console.warn 'Error resizing ACE after an update:', error - # Called from CastButtonView initially and whenever the delay is changed - setAutocastDelay: (@autocastDelay) -> - @createOnCodeChangeHandlers() - createOnCodeChangeHandlers: -> @aceDoc.removeListener 'change', @onCodeChangeMetaHandler if @onCodeChangeMetaHandler - autocastDelay = @autocastDelay ? 3000 - onSignificantChange = [ - _.debounce @setRecompileNeeded, autocastDelay - 100 - @currentAutocastHandler = _.debounce @recompileIfNeeded, autocastDelay - ] + onSignificantChange = [] onAnyChange = [ _.debounce @updateAether, 500 _.debounce @notifyEditingEnded, 1000 @@ -499,11 +648,11 @@ module.exports = class SpellView extends CocoView _.throttle @updateLines, 500 _.throttle @hideProblemAlert, 500 ] - onSignificantChange.push _.debounce @checkRequiredCode, 750 if LevelOptions[@options.level.get('slug')]?.requiredCode - onSignificantChange.push _.debounce @checkSuspectCode, 750 if LevelOptions[@options.level.get('slug')]?.suspectCode + onSignificantChange.push _.debounce @checkRequiredCode, 750 if @options.level.get 'requiredCode' + onSignificantChange.push _.debounce @checkSuspectCode, 750 if @options.level.get 'suspectCode' @onCodeChangeMetaHandler = => return if @eventsSuppressed - Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'code-change', volume: 0.5 + #@playSound 'code-change', volume: 0.5 # Currently not using this sound. if @spellThang @spell.hasChangedSignificantly @getSource(), @spellThang.aether.raw, (hasChanged) => if not @spellThang or hasChanged @@ -511,10 +660,7 @@ module.exports = class SpellView extends CocoView callback() for callback in onAnyChange # Then these @aceDoc.on 'change', @onCodeChangeMetaHandler - setRecompileNeeded: (@recompileNeeded) => - - onCursorActivity: => - @currentAutocastHandler?() + onCursorActivity: => # Used to refresh autocast delay; doesn't do anything at the moment. # Design for a simpler system? # * Keep Aether linting, debounced, on any significant change @@ -616,6 +762,7 @@ module.exports = class SpellView extends CocoView hashValue = aether.raw + aetherProblem.message return if hashValue of @savedProblems @savedProblems[hashValue] = true + # Save new problem @userCodeProblem = new UserCodeProblem() @userCodeProblem.set 'code', aether.raw @@ -640,7 +787,7 @@ module.exports = class SpellView extends CocoView @userCodeProblem.save() null - # Autocast: + # Autocast (preload the world in the background): # Goes immediately if the code is a) changed and b) complete/valid and c) the cursor is at beginning or end of a line # We originally thought it would: # - Go after specified delay if a) and b) but not c) @@ -654,28 +801,36 @@ module.exports = class SpellView extends CocoView endOfLine = cursorPosition.column >= currentLine.length # just typed a semicolon or brace, for example beginningOfLine = not currentLine.substr(0, cursorPosition.column).trim().length # uncommenting code, for example incompleteThis = /^(s|se|sel|self|t|th|thi|this)$/.test currentLine.trim() - # console.log "finished=#{valid and (endOfLine or beginningOfLine) and not incompleteThis}", valid, endOfLine, beginningOfLine, incompleteThis, cursorPosition, currentLine.length, aether, new Date() - 0, currentLine + #console.log "finished=#{valid and (endOfLine or beginningOfLine) and not incompleteThis}", valid, endOfLine, beginningOfLine, incompleteThis, cursorPosition, currentLine.length, aether, new Date() - 0, currentLine if valid and (endOfLine or beginningOfLine) and not incompleteThis - if @autocastDelay > 60000 - @preload() - else - @recompile() + @preload() singleLineCommentRegex: -> - return @_singleLineCommentRegex if @_singleLineCommentRegex - commentStarts = - javascript: '//' - python: '#' - coffeescript: '#' - clojure: ';' - lua: '--' - io: '//' + if @_singleLineCommentRegex + @_singleLineCommentRegex.lastIndex = 0 + return @_singleLineCommentRegex commentStart = commentStarts[@spell.language] or '//' - @_singleLineCommentRegexp ?= new RegExp "[ \t]*#{commentStart}[^\"'\n]*", 'g' - @_singleLineCommentRegexp + @_singleLineCommentRegex = new RegExp "[ \t]*#{commentStart}[^\"'\n]*", 'g' + @_singleLineCommentRegex + + lineWithCodeRegex: -> + if @_lineWithCodeRegex + @_lineWithCodeRegex.lastIndex = 0 + return @_lineWithCodeRegex + commentStart = commentStarts[@spell.language] or '//' + @_lineWithCodeRegex = new RegExp "^[ \t]*(?!( |]t|#{commentStart}))+", 'g' + @_lineWithCodeRegex + + commentOutMyCode: -> + prefix = if @spell.language is 'javascript' then 'return; ' else 'return ' + comment = prefix + commentStarts[@spell.language] preload: -> # Send this code over to the God for preloading, but don't change the cast state. + #console.log 'preload?', @spell.source.indexOf('while'), @spell.source.length, @spellThang?.castAether?.metrics?.statementsExecuted + return if @spell.source.indexOf('while') isnt -1 # If they're working with while-loops, it's more likely to be an incomplete infinite loop, so don't preload. + return if @spell.source.length > 500 # Only preload on really short methods + return if @spellThang?.castAether?.metrics?.statementsExecuted > 2000 # Don't preload if they are running significant amounts of user code oldSource = @spell.source oldSpellThangAethers = {} for thangID, spellThang of @spell.thangs @@ -751,7 +906,18 @@ module.exports = class SpellView extends CocoView onCoordinateSelected: (e) -> return unless @ace.isFocused() and e.x? and e.y? - @ace.insert "{x: #{e.x}, y: #{e.y}}" + if @spell.language is 'python' + @ace.insert "{\"x\": #{e.x}, \"y\": #{e.y}}" + else if @spell.language is 'clojure' + @ace.insert "{:x #{e.x} :y #{e.y}}" + else if @spell.language is 'lua' + @ace.insert "{x=#{e.x}, y=#{e.y}}" + else if @spell.language is 'io' + return + else + @ace.insert "{x: #{e.x}, y: #{e.y}}" + + @highlightCurrentLine() onStatementIndexUpdated: (e) -> @@ -760,7 +926,7 @@ module.exports = class SpellView extends CocoView highlightCurrentLine: (flow) => # TODO: move this whole thing into SpellDebugView or somewhere? - @highlightComments() unless @destroyed + @highlightEntryPoints() unless @destroyed flow ?= @spellThang?.castAether?.flow return unless flow and @thang executed = [] @@ -799,7 +965,7 @@ module.exports = class SpellView extends CocoView @aceSession.removeGutterDecoration row, 'executed' @decoratedGutter[row] = '' lastExecuted = _.last executed - showToolbarView = executed.length and @spellThang.castAether.metrics.statementsExecuted > 3 and not LevelOptions[@options.level.get('slug')]?.hidesCodeToolbar # Hide for a while + showToolbarView = executed.length and @spellThang.castAether.metrics.statementsExecuted > 3 and not @options.level.get 'hidesCodeToolbar' # Hide for a while showToolbarView = false # TODO: fix toolbar styling in new design to have some space for it if showToolbarView @@ -836,17 +1002,62 @@ module.exports = class SpellView extends CocoView @debugView?.setVariableStates {} unless gotVariableStates null - highlightComments: -> - return # Slightly buggy and not that great, so let's not do it. - lines = $(@ace.container).find('.ace_text-layer .ace_line_group') + highlightEntryPoints: -> + # Put a yellow arrow in the gutter pointing to each place we expect them to put in code. + # Usually, this is indicated by a blank line after a comment line, except for the first comment lines. + # If we need to indicate an entry point on a line that has code, we use ∆ in a comment on that line. + # If the entry point line has been changed (beyond the most basic shifted lines), we don't point it out. + lines = @aceDoc.$lines + originalLines = @spell.originalSource.split '\n' session = @aceSession - top = Math.floor @ace.renderer.getScrollTopRow() - $(@ace.container).find('.ace_gutter-cell').each (index, el) -> - line = $(lines[index]) - index = index - top - session.removeGutterDecoration index, 'comment-line' - if line.find('.ace_comment').length - session.addGutterDecoration index, 'comment-line' + commentStart = commentStarts[@spell.language] or '//' + seenAnEntryPoint = false + previousLine = null + previousLineHadComment = false + previousLineHadCode = false + previousLineWasBlank = false + pastIntroComments = false + for line, index in lines + session.removeGutterDecoration index, 'entry-point' + session.removeGutterDecoration index, 'next-entry-point' + + lineHasComment = @singleLineCommentRegex().test line + lineHasCode = line.trim()[0] and not _.string.startsWith line.trim(), commentStart + lineIsBlank = /^[ \t]*$/.test line + lineHasExplicitMarker = line.indexOf('∆') isnt -1 + + originalLine = originalLines[index] + lineHasChanged = line isnt originalLine + + isEntryPoint = lineIsBlank and previousLineHadComment and not previousLineHadCode and pastIntroComments + if isEntryPoint and lineHasChanged + # It might just be that the line was shifted around by the player inserting more code. + # We also look for the unchanged comment line in a new position to find what line we're really on. + movedIndex = originalLines.indexOf previousLine + if movedIndex isnt -1 and line is originalLines[movedIndex + 1] + lineHasChanged = false + else + isEntryPoint = false + + if lineHasExplicitMarker + if lineHasChanged + if originalLines.indexOf(line) isnt -1 + lineHasChanged = false + isEntryPoint = true + else + isEntryPoint = true + + if isEntryPoint + session.addGutterDecoration index, 'entry-point' + unless seenAnEntryPoint + session.addGutterDecoration index, 'next-entry-point' + seenAnEntryPoint = true + + previousLine = line + previousLineHadComment = lineHasComment + previousLineHadCode = lineHasCode + previousLineWasBlank = lineIsBlank + pastIntroComments ||= lineHasCode or previousLineWasBlank onAnnotationClick: -> # @ is the gutter element @@ -888,6 +1099,8 @@ module.exports = class SpellView extends CocoView _.delay (=> @resize()), 500 + 100 # Wait $level-resize-transition-time, plus a bit. onWindowResize: (e) => + @spellPaletteHeight = null + $('#spell-palette-view').css 'height', 'auto' # Let it go back to controlling its own height _.delay (=> @resize?()), 500 + 100 # Wait $level-resize-transition-time, plus a bit. resize: -> @@ -934,7 +1147,7 @@ module.exports = class SpellView extends CocoView checkRequiredCode: => return if @destroyed source = @getSource().replace @singleLineCommentRegex(), '' - requiredCodeFragments = LevelOptions[@options.level.get('slug')].requiredCode + requiredCodeFragments = @options.level.get 'requiredCode' for requiredCodeFragment in requiredCodeFragments # Could make this obey regular expressions like suspectCode if needed if source.indexOf(requiredCodeFragment) is -1 @@ -946,10 +1159,11 @@ module.exports = class SpellView extends CocoView checkSuspectCode: => return if @destroyed source = @getSource().replace @singleLineCommentRegex(), '' - suspectCodeFragments = LevelOptions[@options.level.get('slug')].suspectCode + suspectCodeFragments = @options.level.get 'suspectCode' detectedSuspectCodeFragmentNames = [] for suspectCodeFragment in suspectCodeFragments - if suspectCodeFragment.pattern.test source + pattern = new RegExp suspectCodeFragment.pattern, 'm' + if pattern.test source @warnedCodeFragments ?= {} unless @warnedCodeFragments[suspectCodeFragment.name] Backbone.Mediator.publish 'tome:suspect-code-fragment-added', codeFragment: suspectCodeFragment.name, codeLanguage: @spell.language @@ -970,5 +1184,15 @@ module.exports = class SpellView extends CocoView @aceSession?.selection.off 'changeCursor', @onCursorActivity @destroyAceEditor(@ace) @debugView?.destroy() + @toolbarView?.destroy() + @zatanna.addSnippets [], @editorLang if @editorLang? $(window).off 'resize', @onWindowResize super() + +commentStarts = + javascript: '//' + python: '#' + coffeescript: '#' + clojure: ';' + lua: '--' + io: '//' diff --git a/app/views/play/level/tome/TomeView.coffee b/app/views/play/level/tome/TomeView.coffee index bd891ab89..5d4da09e5 100644 --- a/app/views/play/level/tome/TomeView.coffee +++ b/app/views/play/level/tome/TomeView.coffee @@ -1,7 +1,7 @@ # There's one TomeView per Level. It has: # - a CastButtonView, which has # - a cast button -# - an autocast settings options button +# - a submit/done button # - for each spell (programmableMethod): # - a Spell, which has # - a list of Thangs that share that Spell, with one aether per Thang per Spell @@ -58,11 +58,11 @@ module.exports = class TomeView extends CocoView afterRender: -> super() @worker = @createWorker() - #programmableThangs = _.filter @options.thangs, (t) -> t.isProgrammable and t.spriteName isnt 'Hero Placeholder' - programmableThangs = _.filter @options.thangs, 'isProgrammable' + programmableThangs = _.filter @options.thangs, (t) -> t.isProgrammable and t.programmableMethods @createSpells programmableThangs, programmableThangs[0]?.world # Do before spellList, thangList, and castButton - @spellList = @insertSubView new SpellListView spells: @spells, supermodel: @supermodel, level: @options.level - @castButton = @insertSubView new CastButtonView spells: @spells, levelID: @options.levelID, session: @options.session + unless @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] + @spellList = @insertSubView new SpellListView spells: @spells, supermodel: @supermodel, level: @options.level + @castButton = @insertSubView new CastButtonView spells: @spells, level: @options.level, session: @options.session @teamSpellMap = @generateTeamSpellMap(@spells) unless programmableThangs.length @cast() @@ -73,14 +73,14 @@ module.exports = class TomeView extends CocoView onNewWorld: (e) -> thangs = _.filter e.world.thangs, 'inThangList' - programmableThangs = _.filter thangs, 'isProgrammable' + programmableThangs = _.filter thangs, (t) -> t.isProgrammable and t.programmableMethods @createSpells programmableThangs, e.world - @spellList.adjustSpells @spells + @spellList?.adjustSpells @spells onCommentMyCode: (e) -> for spellKey, spell of @spells when spell.canWrite() console.log 'Commenting out', spellKey - commentedSource = 'return; // Commented out to stop infinite loop.\n' + spell.getSource() + commentedSource = spell.view.commentOutMyCode() + 'Commented out to stop infinite loop.\n' + spell.getSource() spell.view.updateACEText commentedSource spell.view.recompile false @cast() @@ -133,6 +133,7 @@ module.exports = class TomeView extends CocoView language: language spectateView: @options.spectateView spectateOpponentCodeLanguage: @options.spectateOpponentCodeLanguage + observing: @options.observing levelID: @options.levelID level: @options.level @@ -162,15 +163,19 @@ module.exports = class TomeView extends CocoView if realTime sessionState.submissionCount = (sessionState.submissionCount ? 0) + 1 sessionState.flagHistory = _.filter sessionState.flagHistory ? [], (event) => event.team isnt (@options.session.get('team') ? 'humans') + sessionState.lastUnsuccessfulSubmissionTime = new Date() if @options.level.get 'replayable' @options.session.set 'state', sessionState - Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload, realTime: realTime, submissionCount: sessionState.submissionCount ? 0, flagHistory: sessionState.flagHistory ? [] + difficulty = sessionState.difficulty ? 0 + if @options.observing + difficulty = Math.max 0, difficulty - 1 # Show the difficulty they won, not the next one. + Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload, realTime: realTime, submissionCount: sessionState.submissionCount ? 0, flagHistory: sessionState.flagHistory ? [], difficulty: difficulty onToggleSpellList: (e) -> - @spellList.rerenderEntries() - @spellList.$el.toggle() + @spellList?.rerenderEntries() + @spellList?.$el.toggle() onSpellViewClick: (e) -> - @spellList.$el.hide() + @spellList?.$el.hide() onClick: (e) -> Backbone.Mediator.publish 'tome:focus-editor', {} unless $(e.target).parents('.popover').length @@ -187,7 +192,7 @@ module.exports = class TomeView extends CocoView @castButton?.$el.hide() onSpriteSelected: (e) -> - return if @spellView and @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] # Never deselect the hero in the Tome. + return if @spellView and @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] # Never deselect the hero in the Tome. thang = e.thang spellName = e.spellName @spellList?.$el.hide() @@ -203,10 +208,10 @@ module.exports = class TomeView extends CocoView @spellTabView = spell.tabView @$el.find('#' + @spellView.id).after(@spellView.el).remove() @$el.find('#' + @spellTabView.id).after(@spellTabView.el).remove() - @castButton.attachTo @spellView + @castButton?.attachTo @spellView Backbone.Mediator.publish 'tome:spell-shown', thang: thang, spell: spell @updateSpellPalette thang, spell - @spellList.setThangAndSpell thang, spell + @spellList?.setThangAndSpell thang, spell @spellView?.setThang thang @spellTabView?.setThang thang diff --git a/app/views/game-menu/GameMenuModal.coffee b/app/views/play/menu/GameMenuModal.coffee similarity index 56% rename from app/views/game-menu/GameMenuModal.coffee rename to app/views/play/menu/GameMenuModal.coffee index 602f6c554..90c5b0dfa 100644 --- a/app/views/game-menu/GameMenuModal.coffee +++ b/app/views/play/menu/GameMenuModal.coffee @@ -1,10 +1,11 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/game-menu/game-menu-modal' +AuthModal = require 'views/core/AuthModal' +template = require 'templates/play/menu/game-menu-modal' submenuViews = [ - require 'views/game-menu/SaveLoadView' - require 'views/game-menu/OptionsView' - require 'views/game-menu/GuideView' - require 'views/game-menu/MultiplayerView' + require 'views/play/menu/SaveLoadView' + require 'views/play/menu/OptionsView' + require 'views/play/menu/GuideView' + require 'views/play/menu/MultiplayerView' ] module.exports = class GameMenuModal extends ModalView @@ -18,6 +19,7 @@ module.exports = class GameMenuModal extends ModalView 'shown.bs.tab #game-menu-nav a': 'onTabShown' 'click #change-hero-tab': -> @trigger 'change-hero' 'click #close-modal': 'hide' + 'click .auth-tab': 'onClickSignupButton' constructor: (options) -> super options @@ -29,9 +31,11 @@ module.exports = class GameMenuModal extends ModalView getRenderData: (context={}) -> context = super(context) docs = @options.level.get('documentation') ? {} - submenus = ["options", "save-load", "guide", "multiplayer"] + submenus = ['guide', 'options', 'save-load', 'multiplayer'] submenus = _.without submenus, 'guide' unless docs.specificArticles?.length or docs.generalArticles?.length submenus = _.without submenus, 'save-load' unless me.isAdmin() or /https?:\/\/localhost/.test(window.location.href) + submenus = _.without submenus, 'multiplayer' unless me.isAdmin() or @level?.get('type') in ['ladder', 'hero-ladder', 'course-ladder'] + @includedSubmenus = submenus context.showTab = @options.showTab ? submenus[0] context.submenus = submenus context.iconMap = @@ -39,16 +43,17 @@ module.exports = class GameMenuModal extends ModalView 'guide': 'list' 'save-load': 'floppy-disk' 'multiplayer': 'globe' + context.showsChooseHero = (@level?.get('type') not in ['course', 'course-ladder']) and (@options.levelID not in ['zero-sum']) context afterRender: -> super() @insertSubView new submenuView @options for submenuView in submenuViews - if @options.showTab - firstView = switch @options.showTab - when 'multiplayer' then @subviews.multiplayer_view - unless firstView? - firstView = (@subviews.options_view) + firstView = switch @options.showTab + when 'multiplayer' then @subviews.multiplayer_view + when 'guide' then @subviews.guide_view + else + if 'guide' in @includedSubmenus then @subviews.guide_view else @subviews.options_view firstView.$el.addClass 'active' firstView.onShown?() @playSound 'game-menu-open' @@ -56,10 +61,18 @@ module.exports = class GameMenuModal extends ModalView onTabShown: (e) -> @playSound 'game-menu-tab-switch' - @subviews[e.target.hash.substring(1).replace(/-/g, '_')].onShown?() + shownSubviewKey = e.target.hash.substring(1).replace(/-/g, '_') + @subviews[shownSubviewKey].onShown?() + subview.onHidden?() for subviewKey, subview of @subviews when subviewKey isnt shownSubviewKey onHidden: -> super() subview.onHidden?() for subviewKey, subview of @subviews @playSound 'game-menu-close' Backbone.Mediator.publish 'music-player:exit-menu', {} + + onClickSignupButton: (e) -> + window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Game Menu', level: @options.levelID + # TODO: Default already seems to be prevented. Need to be explicit? + e.preventDefault() + @openModalView new AuthModal {mode: 'signup'} diff --git a/app/views/play/menu/GuideView.coffee b/app/views/play/menu/GuideView.coffee new file mode 100644 index 000000000..9e3f72fbf --- /dev/null +++ b/app/views/play/menu/GuideView.coffee @@ -0,0 +1,145 @@ +CocoView = require 'views/core/CocoView' +template = require 'templates/play/menu/guide-view' +Article = require 'models/Article' +SubscribeModal = require 'views/core/SubscribeModal' +utils = require 'core/utils' + +# let's implement this once we have the docs database schema set up + +module.exports = class LevelGuideView extends CocoView + template: template + id: 'guide-view' + className: 'tab-pane' + helpVideoHeight: '295' + helpVideoWidth: '471' + + events: + 'click .start-subscription-button': 'clickSubscribe' + + constructor: (options) -> + @levelSlug = options.level.get('slug') + @sessionID = options.session.get('_id') + @requiresSubscription = not me.isPremium() + @helpVideos = options.level.get('helpVideos') ? [] + @trackedHelpVideoStart = @trackedHelpVideoFinish = false + # A/B Testing video tutorial styles + @helpVideosIndex = me.getVideoTutorialStylesIndex(@helpVideos.length) + @helpVideo = @helpVideos[@helpVideosIndex] if @helpVideos.length > 0 + @videoLocked = not @helpVideo?.free and @requiresSubscription + + @firstOnly = options.firstOnly + @docs = options?.docs ? options.level.get('documentation') ? {} + general = @docs.generalArticles or [] + specific = @docs.specificArticles or [] + + articles = options.supermodel.getModels(Article) + articleMap = {} + articleMap[article.get('original')] = article for article in articles + general = (articleMap[ref.original] for ref in general) + general = (article.attributes for article in general when article) + + @docs = specific.concat(general) + @docs = $.extend(true, [], @docs) + @docs = [@docs[0]] if @firstOnly and @docs[0] + doc.html = marked(utils.i18n doc, 'body') for doc in @docs + doc.name = (utils.i18n doc, 'name') for doc in @docs + doc.slug = _.string.slugify(doc.name) for doc in @docs + super() + + destroy: -> + if @vimeoListenerAttached + if window.addEventListener + window.removeEventListener('message', @onMessageReceived, false) + else + window.detachEvent('onmessage', @onMessageReceived, false) + super() + + getRenderData: -> + c = super() + c.docs = @docs + c.showVideo = @helpVideos.length > 0 + c.videoLocked = @videoLocked + c + + afterRender: -> + super() + if @docs.length is 1 and @helpVideos.length > 0 + @setupVideoPlayer() unless @videoLocked + else + # incredible hackiness. Getting bootstrap tabs to work shouldn't be this complex + @$el.find('.nav-tabs li:first').addClass('active') + @$el.find('.tab-content .tab-pane:first').addClass('active') + @$el.find('.nav-tabs a').click(@clickTab) + @playSound 'guide-open' + + clickSubscribe: (e) -> + level = @levelSlug # Save ref to level slug + @openModalView new SubscribeModal() + window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'help video clicked', level: level + + clickTab: (e) => + @$el.find('li.active').removeClass('active') + @playSound 'guide-tab-switch' + + afterInsert: -> + super() + Backbone.Mediator.publish 'level:docs-shown', {} + + onHidden: -> + createjs?.Sound?.setVolume?(@volume ? ( me.get('volume') ? 1.0)) + Backbone.Mediator.publish 'level:docs-hidden', {} + + onShown: -> + # TODO: Disable sound only when video is playing? + @volume ?= me.get('volume') ? 1.0 + createjs?.Sound?.setVolume(0.0) + + onStartHelpVideo: -> + unless @trackedHelpVideoStart + window.tracker?.trackEvent 'Start help video', level: @levelSlug, ls: @sessionID, style: @helpVideo?.style + @trackedHelpVideoStart = true + + onFinishHelpVideo: -> + unless @trackedHelpVideoFinish + window.tracker?.trackEvent 'Finish help video', level: @levelSlug, ls: @sessionID, style: @helpVideo?.style + @trackedHelpVideoFinish = true + + setupVideoPlayer: () -> + return unless @helpVideo + # Always use HTTPS + # TODO: Not specifying the protocol should work based on Vimeo docs, but breaks postMessage/eventing in practice. + url = "https:" + @helpVideo.url.substr @helpVideo.url.indexOf '/' + @setupVimeoVideoPlayer url + + setupVimeoVideoPlayer: (helpVideoURL) -> + # Setup Vimeo player + # https://developer.vimeo.com/player/js-api#universal-with-postmessage + + # Create Vimeo iframe player + tag = document.createElement('iframe') + tag.id = 'help-video-player' + tag.src = helpVideoURL + "?api=1&badge=0&byline=0&portrait=0&title=0" + tag.height = @helpVideoHeight + tag.width = @helpVideoWidth + tag.frameborder = '0' + @$el.find('#help-video-player').replaceWith(tag) + + @onMessageReceived = (e) => + data = JSON.parse(e.data) + if data.event is 'ready' + # Vimeo player is ready, can now hook up other events + # https://developer.vimeo.com/player/js-api#events + player = $('#help-video-player')[0] + player.contentWindow.postMessage JSON.stringify(method: 'addEventListener', value: 'play'), helpVideoURL + player.contentWindow.postMessage JSON.stringify(method: 'addEventListener', value: 'finish'), helpVideoURL + else if data.event is 'play' + @onStartHelpVideo?() + else if data.event is 'finish' + @onFinishHelpVideo?() + + # Listen for Vimeo player 'ready' + if window.addEventListener + window.addEventListener('message', @onMessageReceived, false) + else + window.attachEvent('onmessage', @onMessageReceived, false) + @vimeoListenerAttached = true diff --git a/app/views/game-menu/InventoryModal.coffee b/app/views/play/menu/InventoryModal.coffee similarity index 66% rename from app/views/game-menu/InventoryModal.coffee rename to app/views/play/menu/InventoryModal.coffee index b4dbdd3b6..0f6b358d0 100644 --- a/app/views/game-menu/InventoryModal.coffee +++ b/app/views/play/menu/InventoryModal.coffee @@ -1,5 +1,5 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/game-menu/inventory-modal' +template = require 'templates/play/menu/inventory-modal' buyGemsPromptTemplate = require 'templates/play/modal/buy-gems-prompt' {me} = require 'core/auth' ThangType = require 'models/ThangType' @@ -8,8 +8,8 @@ ItemView = require './ItemView' SpriteBuilder = require 'lib/sprites/SpriteBuilder' ItemDetailsView = require 'views/play/modal/ItemDetailsView' Purchase = require 'models/Purchase' -LevelOptions = require 'lib/LevelOptions' BuyGemsModal = require 'views/play/modal/BuyGemsModal' +AuthModal = require 'views/core/AuthModal' hasGoneFullScreenOnce = false @@ -18,6 +18,7 @@ module.exports = class InventoryModal extends ModalView className: 'modal fade play-modal' template: template slots: ['head', 'eyes', 'neck', 'torso', 'wrists', 'gloves', 'left-ring', 'right-ring', 'right-hand', 'left-hand', 'waist', 'feet', 'programming-book', 'pet', 'minion', 'flag'] #, 'misc-0', 'misc-1'] # TODO: bring in misc slot(s) again when we have space + ringSlots: ['left-ring', 'right-ring'] closesOnClickOutside: false # because draggable somehow triggers hide when you don't drag onto a draggable events: @@ -35,6 +36,7 @@ module.exports = class InventoryModal extends ModalView 'click #close-modal': 'hide' 'click .buy-gems-prompt-button': 'onBuyGemsPromptButtonClicked' 'click': 'onClickedSomewhere' + 'update #unequipped .nano': 'onScrollUnequipped' shortcuts: 'esc': 'clearSelection' @@ -44,6 +46,7 @@ module.exports = class InventoryModal extends ModalView #- Setup initialize: (options) -> + @onScrollUnequipped = _.throttle(_.bind(@onScrollUnequipped, @), 200) super(arguments...) @items = new CocoCollection([], {model: ThangType}) # TODO: switch to item store loading system? @@ -70,6 +73,7 @@ module.exports = class InventoryModal extends ModalView @equipment = $.extend true, {}, @equipment @requireLevelEquipment() @itemGroups = {} + @itemGroups.requiredPurchaseItems = new Backbone.Collection() @itemGroups.availableItems = new Backbone.Collection() @itemGroups.restrictedItems = new Backbone.Collection() @itemGroups.lockedItems = new Backbone.Collection() @@ -91,14 +95,45 @@ module.exports = class InventoryModal extends ModalView locked = not (item.get('original') in me.items()) #locked = false if me.get('slug') is 'nick' - if not item.getFrontFacingStats().props.length and not _.size(item.getFrontFacingStats().stats) and locked # Temp: while there are placeholder items - null # Don't put into a collection - else if locked and item.get('slug') isnt 'simple-boots' - @itemGroups.lockedItems.add(item) + allRestrictedGear = _.flatten(_.values(@options.level.get('restrictedGear') ? {})) + restricted = item.get('original') in allRestrictedGear + + # TODO: make this re-use result of computation of updateLevelRequiredItems, which we can only do after heroClass is ready... + requiredToPurchase = false + if requiredGear = @options.level.get('requiredGear') + inCampaignView = $('#campaign-view').length + unless gearSlugs[item.get('original')] is 'tarnished-bronze-breastplate' and inCampaignView and @options.level.get('slug') is 'the-raised-sword' + for slot in item.getAllowedSlots() + continue unless requiredItems = requiredGear[slot] + continue if @equipment[slot] and @equipment[slot] not in allRestrictedGear and slot not in @ringSlots + # Point out that they must buy it if they haven't bought any of the required items for that slot, and it's the first one. + if item.get('original') is requiredItems[0] and not _.find(requiredItems, (requiredItem) -> me.ownsItem requiredItem) + requiredToPurchase = true + break + + if requiredToPurchase and locked and not item.get('gems') + # Either one of two things has happened: + # 1. There's a bug and the player doesn't have a required item they should have. + # 2. The player is trying to play a level they haven't unlocked. + # We'll just pretend they own it so that they don't get stuck. + application.tracker?.trackEvent 'Required Item Locked', level: @options.level.get('slug'), label: @options.level.get('slug'), item: item.get('name'), playerLevel: me.level(), levelUnlocked: me.ownsLevel @options.level.get('original') + locked = false + + placeholder = not item.getFrontFacingStats().props.length and not _.size(item.getFrontFacingStats().stats) + + if placeholder and locked # The item is not complete, so don't put it into a collection. + null + else if locked and requiredToPurchase item.classes.push 'locked' - item.classes.push 'silhouette' if item.isSilhouettedItem() - item.classes.push 'hidden' unless item.get('gems') - else if item.get('slug') in _.values(LevelOptions[@options.levelID]?.restrictedGear ? {}) + @itemGroups.requiredPurchaseItems.add item + else if locked + item.classes.push 'locked' + if item.isSilhouettedItem() or not item.get('gems') + # Don't even load/show these--don't add to a collection. (Bandwidth optimization.) + null + else + @itemGroups.lockedItems.add(item) + else if restricted @itemGroups.restrictedItems.add(item) item.classes.push 'restricted' else @@ -119,6 +154,7 @@ module.exports = class InventoryModal extends ModalView context.itemGroups = @itemGroups context.slots = @slots context.selectedHero = @selectedHero + context.selectedHeroClass = @selectedHero?.get('heroClass') context.equipment = _.clone @equipment context.equipment[slot] = @items.findWhere {original: itemOriginal} for slot, itemOriginal of context.equipment context.gems = me.gems() @@ -138,6 +174,7 @@ module.exports = class InventoryModal extends ModalView @requireLevelEquipment() @$el.find('.nano').nanoScroller({alwaysVisible: true}) @onSelectionChanged() + @onEquipmentChanged() afterInsert: -> super() @@ -145,7 +182,6 @@ module.exports = class InventoryModal extends ModalView @canvasHeight = @$el.find('canvas').innerHeight() @inserted = true @requireLevelEquipment() - @onEquipmentChanged() #- Draggable logic @@ -187,8 +223,13 @@ module.exports = class InventoryModal extends ModalView makeEquippedSlotDraggable: (slot) -> unequip = => - @unequipItemFromSlot slot + itemEl = @unequipItemFromSlot slot + selectedSlotItemID = itemEl.data('item-id') + item = @items.get(selectedSlotItemID) @requireLevelEquipment() + @showItemDetails(item, 'equip') + @onSelectionChanged() + @onEquipmentChanged() shouldStayEquippedWhenDropped = (isValidDrop) -> pos = $(@).position() revert = Math.abs(pos.left) < $(@).outerWidth() and Math.abs(pos.top) < $(@).outerHeight() @@ -210,14 +251,14 @@ module.exports = class InventoryModal extends ModalView onItemSlotClick: (e) -> return if @remainingRequiredEquipment?.length # Don't let them select a slot if we need them to first equip some require gear. - @playSound 'menu-button-click' + #@playSound 'menu-button-click' @selectItemSlot($(e.target).closest('.item-slot')) onUnequippedItemClick: (e) -> return if @justDoubleClicked + return if @justClickedEquipItemButton itemEl = $(e.target).closest('.item') - return if itemEl.hasClass('silhouette') - @playSound 'menu-button-click' + #@playSound 'menu-button-click' @selectUnequippedItem(itemEl) onUnequippedItemDoubleClick: (e) -> @@ -236,6 +277,8 @@ module.exports = class InventoryModal extends ModalView itemEl = $(e.target).closest('.item') @selectUnequippedItem(itemEl) @equipSelectedItem() + @justClickedEquipItemButton = true + _.defer => @justClickedEquipItemButton = false #- Select/equip higher-level, all encompassing methods the callbacks all use @@ -259,7 +302,12 @@ module.exports = class InventoryModal extends ModalView selectedItem = @items.get(selectedItemEl.data('item-id')) return unless selectedItem allowedSlots = selectedItem.getAllowedSlots() - slotEl = @$el.find(".item-slot[data-slot='#{allowedSlots[0]}']") + firstSlot = unequippedSlot = null + for allowedSlot in allowedSlots + slotEl = @$el.find(".item-slot[data-slot='#{allowedSlot}']") + firstSlot ?= slotEl + unequippedSlot ?= slotEl unless slotEl.find('img').length + slotEl = unequippedSlot ? firstSlot selectedItemEl.effect('transfer', to: slotEl, duration: 500, easing: 'easeOutCubic') unequipped = @unequipItemFromSlot(slotEl) selectedItemEl.addClass('equipped') @@ -300,6 +348,8 @@ module.exports = class InventoryModal extends ModalView itemIDToUnequip = itemEl.data('item-id') return unless itemIDToUnequip itemEl.remove() + item = @items.get itemIDToUnequip + item.classes = _.without item.classes, 'equipped' @$el.find("#unequipped .item[data-item-id=#{itemIDToUnequip}]").removeClass('equipped') deselectAllSlots: -> @@ -331,7 +381,7 @@ module.exports = class InventoryModal extends ModalView @$el.find("##{showExtra}-item-viewed").removeClass('secret') hideItemDetails: -> - @itemDetailsView.setItem(null) + @itemDetailsView?.setItem(null) @$el.find('#item-details-extra > *').addClass('secret') getCurrentEquipmentConfig: -> @@ -345,55 +395,77 @@ module.exports = class InventoryModal extends ModalView config requireLevelEquipment: -> - # This is temporary, until we have a more general way of awarding items and configuring required/restricted items per level. - requiredGear = LevelOptions[@options.levelID]?.requiredGear ? {} - restrictedGear = LevelOptions[@options.levelID]?.restrictedGear ? {} - if @inserted - if @supermodel.finished() - equipment = @getCurrentEquipmentConfig() # Make sure @equipment is updated - else - equipment = @equipment - hadRequired = @remainingRequiredEquipment?.length - @remainingRequiredEquipment = [] - @$el.find('.should-equip').removeClass('should-equip') - inWorldMap = $('#world-map-view').length - if heroClass = @selectedHero?.get('heroClass') - for slot, item of _.clone equipment - itemModel = @items.findWhere original: item - unless itemModel and heroClass in itemModel.classes - console.log 'Unequipping', itemModel.get('heroClass'), 'item', itemModel.get('name'), 'from slot due to class restrictions.' - @unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']") - delete equipment[slot] - for slot, item of restrictedGear + # This is called frequently to make sure the player isn't using any restricted items and knows she must equip any required items. + return unless @inserted + equipment = if @supermodel.finished() then @getCurrentEquipmentConfig() else @equipment # Make sure we're using latest equipment. + hadRequired = @remainingRequiredEquipment?.length + @remainingRequiredEquipment = [] + @$el.find('.should-equip').removeClass('should-equip') + @unequipClassRestrictedItems equipment + @unequipLevelRestrictedItems equipment + @updateLevelRequiredItems equipment + if hadRequired and not @remainingRequiredEquipment.length + @endHighlight() + @highlightElement '#play-level-button', duration: 5000 + $('#play-level-button').prop('disabled', @remainingRequiredEquipment.length > 0) + + unequipClassRestrictedItems: (equipment) -> + return unless @supermodel.finished() and heroClass = @selectedHero?.get 'heroClass' + for slot, item of _.clone equipment + itemModel = @items.findWhere original: item + unless itemModel and heroClass in itemModel.classes + console.log 'Unequipping', itemModel.get('heroClass'), 'item', itemModel.get('name'), 'from slot due to class restrictions.' + @unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']") + delete equipment[slot] + + unequipLevelRestrictedItems: (equipment) -> + return unless restrictedGear = @options.level.get 'restrictedGear' + for slot, items of restrictedGear + for item in items equipped = equipment[slot] - if equipped and equipped is gear[restrictedGear[slot]] - console.log 'Unequipping restricted item', restrictedGear[slot], 'for', slot, 'before level', @options.levelID + if equipped and equipped is item + console.log 'Unequipping restricted item', equipped, 'for', slot, 'before level', @options.level.get('slug') @unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']") delete equipment[slot] - if (heroClass is 'Warrior' or - (heroClass is 'Ranger' and @options.levelID in ['swift-dagger', 'shrapnel']) or - (heroClass is 'Wizard' and @options.levelID in ['touch-of-death', 'bonemender'])) - # After they switch to a ranger or wizard, we stop being so finicky about gear. - for slot, item of requiredGear - continue if item is 'tarnished-bronze-breastplate' and inWorldMap and @options.levelID is 'the-raised-sword' # Don't tell them they need it until they need it in the level - equipped = equipment[slot] - continue if equipped and not ( - (item is 'crude-builders-hammer' and equipped in [gear['simple-sword'], gear['long-sword'], gear['sharpened-sword'], gear['roughedge']]) or - (item in ['simple-sword', 'long-sword', 'roughedge', 'sharpened-sword'] and equipped is gear['crude-builders-hammer']) or - (item is 'leather-boots' and equipped is gear['simple-boots']) or - (item is 'simple-boots' and equipped is gear['leather-boots']) - ) - itemModel = @items.findWhere {slug: item} - continue unless itemModel - availableSlotSelector = "#unequipped .item[data-item-id='#{itemModel.id}']" - @highlightElement availableSlotSelector, delay: 500, sides: ['right'], rotation: Math.PI / 2 - @$el.find(availableSlotSelector).addClass 'should-equip' - @$el.find("#equipped div[data-slot='#{slot}']").addClass 'should-equip' - @remainingRequiredEquipment.push slot: slot, item: gear[item] - if hadRequired and not @remainingRequiredEquipment.length - @endHighlight() - @highlightElement '#play-level-button', duration: 5000 - $('#play-level-button').prop('disabled', @remainingRequiredEquipment.length > 0) + + updateLevelRequiredItems: (equipment) -> + return unless requiredGear = @options.level.get 'requiredGear' + return unless heroClass = @selectedHero?.get 'heroClass' + + for slot, items of requiredGear when items.length + if slot in @ringSlots + validSlots = @ringSlots + else + validSlots = [slot] + + continue if validSlots.some (slot) -> + equipped = equipment[slot] + equipped in items + + # Actually, just let them play if they have equipped anything in that slot (and we haven't unequipped it due to restrictions). + # Rings often have unique effects, so this rule does not apply to them (they are still required even if there is a non-restricted ring equipped in the slot). + continue if equipment[slot] and slot not in @ringSlots + + items = (item for item in items when heroClass in (@items.findWhere(original: item)?.classes ? [])) + continue unless items.length # If the required items are for another class, then let's not be finicky. + + # We will point out the last (best) element that they own and can use, otherwise the first (cheapest). + bestOwnedItem = _.findLast items, (item) -> me.ownsItem item + item = bestOwnedItem ? items[0] + + # For the Tarnished Bronze Breastplate only, don't tell them they need it until they need it in the level, so we can show how to buy it. + slug = gearSlugs[item] + inCampaignView = $('#campaign-view').length + continue if slug is 'tarnished-bronze-breastplate' and inCampaignView and @options.level.get('slug') is 'the-raised-sword' + + # Now we're definitely requiring and pointing out an item. + itemModel = @items.findWhere {original: item} + availableSlotSelector = "#unequipped .item[data-item-id='#{itemModel.id}']" + @highlightElement availableSlotSelector, delay: 500, sides: ['right'], rotation: Math.PI / 2 + @$el.find(availableSlotSelector).addClass 'should-equip' + @$el.find("#equipped div[data-slot='#{slot}']").addClass 'should-equip' + @remainingRequiredEquipment.push slot: slot, item: item + null setHero: (@selectedHero) -> if @selectedHero.loading @@ -420,15 +492,16 @@ module.exports = class InventoryModal extends ModalView onClickPlayLevel: (e) -> return if @$el.find('#play-level-button').prop 'disabled' + levelSlug = @options.level.get('slug') @playSound 'menu-button-click' @showLoading() ua = navigator.userAgent.toLowerCase() - unless hasGoneFullScreenOnce or (/safari/.test(ua) and not /chrome/.test(ua)) or $(window).height() >= 658 # Min vertical resolution needed at 1366px wide + unless me.isAdmin() or hasGoneFullScreenOnce and (/safari/.test(ua) and not /chrome/.test(ua)) or $(window).height() >= 658 # Min vertical resolution needed at 1366px wide @toggleFullscreen() hasGoneFullScreenOnce = true @updateConfig => @trigger? 'play-click' - window.tracker?.trackEvent 'Inventory Play', category: 'Play Level', ['Google Analytics'] + window.tracker?.trackEvent 'Inventory Play', category: 'Play Level', level: levelSlug updateConfig: (callback, skipSessionSave) -> sessionHeroConfig = @options.session.get('heroConfig') ? {} @@ -437,7 +510,7 @@ module.exports = class InventoryModal extends ModalView patchSession = patchMe = false patchSession ||= not _.isEqual inventory, sessionHeroConfig.inventory sessionHeroConfig.inventory = inventory - if hero = @selectedHero.get('original') + if hero = @selectedHero?.get('original') patchSession ||= not _.isEqual hero, sessionHeroConfig.thangType sessionHeroConfig.thangType = hero patchMe ||= not _.isEqual inventory, lastHeroConfig.inventory @@ -478,15 +551,17 @@ module.exports = class InventoryModal extends ModalView #- ...then rerender key bits @itemGroups.lockedItems.remove(item) + @itemGroups.requiredPurchaseItems.remove(item) # Redo all item sorting to make sure that we don't clobber state changes since last render. equipped = _.values @getCurrentEquipmentConfig() - @sortItem(item, equipped) for item in @items.models + @sortItem(otherItem, equipped) for otherItem in @items.models @renderSelectors('#unequipped', '#gems-count') @requireLevelEquipment() @delegateEvents() @setUpDraggableEventsForAvailableEquipment() @itemDetailsView.setItem(item) + @onScrollUnequipped true Backbone.Mediator.publish 'store:item-purchased', item: item, itemSlug: item.get('slug') else @@ -495,9 +570,12 @@ module.exports = class InventoryModal extends ModalView @$el.one 'click', (e) -> button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0] + askToSignUp: -> + authModal = new AuthModal supermodel: @supermodel + authModal.mode = 'signup' + return @openModalView authModal + askToBuyGems: (unlockButton) -> - if me.getGemPromptGroup() is 'no-prompt' - return @openModalView new BuyGemsModal() @$el.find('.unlock-button').popover 'destroy' popoverTemplate = buyGemsPromptTemplate {} unlockButton.popover( @@ -513,6 +591,7 @@ module.exports = class InventoryModal extends ModalView onBuyGemsPromptButtonClicked: (e) -> @playSound 'menu-button-click' + return @askToSignUp() if me.get('anonymous') @openModalView new BuyGemsModal() onClickedSomewhere: (e) -> @@ -520,26 +599,46 @@ module.exports = class InventoryModal extends ModalView @$el.find('.unlock-button').popover 'destroy' + #- Dynamic portrait loading + + onScrollUnequipped: (forceLoadAll=false) -> + # dynamically load visible items when the user scrolls enough to see them + return if @destroyed + nanoContent = @$el.find('#unequipped .nano-content') + items = nanoContent.find('.item:visible:not(.loaded)') + threshold = nanoContent.height() + 100 + for itemEl in items + itemEl = $(itemEl) + if itemEl.position().top < threshold or forceLoadAll + itemEl.addClass('loaded') + item = @items.get(itemEl.data('item-id')) + itemEl.find('img').attr('src', item.getPortraitURL()) + + #- Paper doll equipment updating onEquipmentChanged: -> - @removeDollImages() heroClass = @selectedHero?.get('heroClass') ? 'Warrior' gender = if @selectedHero?.get('slug') in heroGenders.male then 'male' else 'female' + @$el.find('#hero-image, #hero-image-hair, #hero-image-head, #hero-image-thumb').removeClass().addClass "#{gender} #{heroClass}" equipment = @getCurrentEquipmentConfig() + @onScrollUnequipped() + return unless _.size(equipment) and @supermodel.finished() + @removeDollImages() slotsWithImages = [] for slot, original of equipment item = _.find @items.models, (item) -> item.get('original') is original continue unless dollImages = item?.get('dollImages') - didAdd = @addDollImage slot, dollImages, heroClass, gender - slotsWithImages.push slot if didAdd - @$el.find('#hero-image, #hero-image-hair, #hero-image-head, #hero-image-thumb').removeClass().addClass "#{gender} #{heroClass}" + didAdd = @addDollImage slot, dollImages, heroClass, gender, item + slotsWithImages.push slot if didAdd if item.get('original') isnt '54ea39342b7506e891ca70f2' # Circlet of the Magi needs hair under it @$el.find('#hero-image-hair').toggle not ('head' in slotsWithImages) @$el.find('#hero-image-thumb').toggle not ('gloves' in slotsWithImages) + @equipment = @options.equipment = equipment + removeDollImages: -> @$el.find('.doll-image').remove() - addDollImage: (slot, dollImages, heroClass, gender) -> + addDollImage: (slot, dollImages, heroClass, gender, item) -> heroClass = @selectedHero?.get('heroClass') ? 'Warrior' gender = if @selectedHero?.get('slug') in heroGenders.male then 'male' else 'female' didAdd = false @@ -548,6 +647,11 @@ module.exports = class InventoryModal extends ModalView imageKeys = ["#{gender}#{heroClass}", "#{gender}#{heroClass}Thumb"] else imageKeys = ["#{gender}", "#{gender}Thumb"] + else if heroClass is 'Wizard' and slot is 'torso' + imageKeys = [gender, "#{gender}Back"] + else if heroClass is 'Ranger' and slot is 'head' and item.get('original') in ['5441c2be4e9aeb727cc97105', '5441c3144e9aeb727cc97111'] + # All-class headgear like faux fur hat, viking helmet is abusing ranger glove slot + imageKeys = ["#{gender}Ranger"] else imageKeys = [gender] for imageKey in imageKeys @@ -562,13 +666,16 @@ module.exports = class InventoryModal extends ModalView destroy: -> @$el.find('.unlock-button').popover 'destroy' + @$el.find('.ui-droppable').droppable 'destroy' + @$el.find('.ui-draggable').draggable('destroy').off 'dragstart' + @$el.find('.item-slot').off 'dragstart' @stage?.removeAllChildren() super() heroGenders = - male: ['knight', 'samurai', 'trapper', 'potion-master'] - female: ['captain', 'ninja', 'forest-archer', 'librarian', 'sorcerer'] + male: ['knight', 'samurai', 'trapper', 'potion-master', 'goliath', 'assassin', 'necromancer'] + female: ['captain', 'ninja', 'forest-archer', 'librarian', 'sorcerer', 'raider', 'guardian', 'pixie', 'dark-wizard'] gear = 'simple-boots': '53e237bf53457600003e3f05' @@ -593,3 +700,10 @@ gear = 'enchanted-stick': '544d87188494308424f564f1' 'unholy-tome-i': '546374bc3839c6e02811d308' 'book-of-life-i': '546375653839c6e02811d30b' + 'rough-sense-stone': '54693140a2b1f53ce79443bc' + 'polished-sense-stone': '53e215a253457600003e3eaf' + 'quartz-sense-stone': '54693240a2b1f53ce79443c5' + 'wooden-builders-hammer': '54694ba3a2b1f53ce794444d' + 'simple-wristwatch': '54693797a2b1f53ce79443e9' + +gearSlugs = _.invert gear diff --git a/app/views/game-menu/ItemView.coffee b/app/views/play/menu/ItemView.coffee similarity index 91% rename from app/views/game-menu/ItemView.coffee rename to app/views/play/menu/ItemView.coffee index 17f8c5a52..f3e427d3b 100644 --- a/app/views/game-menu/ItemView.coffee +++ b/app/views/play/menu/ItemView.coffee @@ -1,5 +1,5 @@ CocoView = require 'views/core/CocoView' -template = require 'templates/game-menu/item-view' +template = require 'templates/play/menu/item-view' module.exports = class ItemView extends CocoView className: 'item-view' diff --git a/app/views/game-menu/MultiplayerView.coffee b/app/views/play/menu/MultiplayerView.coffee similarity index 96% rename from app/views/game-menu/MultiplayerView.coffee rename to app/views/play/menu/MultiplayerView.coffee index 0f80d802d..435dbccd1 100644 --- a/app/views/game-menu/MultiplayerView.coffee +++ b/app/views/play/menu/MultiplayerView.coffee @@ -1,5 +1,5 @@ CocoView = require 'views/core/CocoView' -template = require 'templates/game-menu/multiplayer-view' +template = require 'templates/play/menu/multiplayer-view' {me} = require 'core/auth' ThangType = require 'models/ThangType' LadderSubmissionView = require 'views/play/common/LadderSubmissionView' @@ -27,7 +27,7 @@ module.exports = class MultiplayerView extends CocoView @levelID = @level?.get 'slug' @session = options.session @listenTo @session, 'change:multiplayer', @updateLinkSection - @watchRealTimeSessions() if @level?.get('type') in ['hero-ladder'] + @watchRealTimeSessions() if @level?.get('type') in ['hero-ladder', 'course-ladder'] and me.isAdmin() destroy: -> @realTimeSessions?.off 'add', @onRealTimeSessionAdded @@ -42,12 +42,12 @@ module.exports = class MultiplayerView extends CocoView c.team = @session.get 'team' c.levelSlug = @levelID # For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet. - if @level?.get('type') in ['ladder', 'hero-ladder'] + if @level?.get('type') in ['ladder', 'hero-ladder', 'course-ladder'] c.ladderGame = true c.readyToRank = @session?.readyToRank() # Real-time multiplayer stuff - if @level?.get('type') in ['hero-ladder'] + if @level?.get('type') in ['hero-ladder', 'course-ladder'] and me.isAdmin() c.levelID = @session.get('levelID') c.realTimeSessions = @realTimeSessions c.currentRealTimeSession = @currentRealTimeSession if @currentRealTimeSession @@ -78,7 +78,7 @@ module.exports = class MultiplayerView extends CocoView updateLinkSection: -> multiplayer = @$el.find('#multiplayer').prop('checked') la = @$el.find('#link-area') - la.toggle if @level?.get('type') in ['ladder', 'hero-ladder'] then false else Boolean(multiplayer) + la.toggle if @level?.get('type') in ['ladder', 'hero-ladder', 'course-ladder'] then false else Boolean(multiplayer) true onHidden: -> diff --git a/app/views/game-menu/OptionsView.coffee b/app/views/play/menu/OptionsView.coffee similarity index 94% rename from app/views/game-menu/OptionsView.coffee rename to app/views/play/menu/OptionsView.coffee index e0d83ec2f..205057065 100644 --- a/app/views/game-menu/OptionsView.coffee +++ b/app/views/play/menu/OptionsView.coffee @@ -1,5 +1,5 @@ CocoView = require 'views/core/CocoView' -template = require 'templates/game-menu/options-view' +template = require 'templates/play/menu/options-view' {me} = require 'core/auth' ThangType = require 'models/ThangType' User = require 'models/User' @@ -20,7 +20,6 @@ module.exports = class OptionsView extends CocoView events: 'change #option-music': 'updateMusic' - 'change #option-autorun-delay': 'updateAutorun' 'change #option-key-bindings': 'updateInvisibles' 'change #option-key-bindings': 'updateKeyBindings' 'change #option-indent-guides': 'updateIndentGuides' @@ -44,7 +43,6 @@ module.exports = class OptionsView extends CocoView @aceConfig = _.defaults @aceConfig, @defaultConfig c.aceConfig = @aceConfig c.music = me.get('music', true) - c.autorunDelay = me.get('autocastDelay') ? 5000 c afterRender: -> @@ -80,9 +78,6 @@ module.exports = class OptionsView extends CocoView updateMusic: -> me.set 'music', @$el.find('#option-music').prop('checked') - updateAutorun: -> - me.set 'autocastDelay', parseInt(@$el.find('#option-autorun-delay').val()) - updateInvisibles: -> @aceConfig.invisibles = @$el.find('#option-invisibles').prop('checked') diff --git a/app/views/game-menu/SaveLoadView.coffee b/app/views/play/menu/SaveLoadView.coffee similarity index 91% rename from app/views/game-menu/SaveLoadView.coffee rename to app/views/play/menu/SaveLoadView.coffee index 7e94b4b9d..f396db5b9 100644 --- a/app/views/game-menu/SaveLoadView.coffee +++ b/app/views/play/menu/SaveLoadView.coffee @@ -1,5 +1,5 @@ CocoView = require 'views/core/CocoView' -template = require 'templates/game-menu/save-load-view' +template = require 'templates/play/menu/save-load-view' {me} = require 'core/auth' ThangType = require 'models/ThangType' diff --git a/app/views/play/modal/BuyGemsModal.coffee b/app/views/play/modal/BuyGemsModal.coffee index 73fbe1cfb..1ed6bdd8f 100644 --- a/app/views/play/modal/BuyGemsModal.coffee +++ b/app/views/play/modal/BuyGemsModal.coffee @@ -2,6 +2,7 @@ ModalView = require 'views/core/ModalView' template = require 'templates/play/modal/buy-gems-modal' stripeHandler = require 'core/services/stripe' utils = require 'core/utils' +SubscribeModal = require 'views/core/SubscribeModal' module.exports = class BuyGemsModal extends ModalView id: 'buy-gems-modal' @@ -20,21 +21,29 @@ module.exports = class BuyGemsModal extends ModalView 'stripe:received-token': 'onStripeReceivedToken' events: - 'click .product button': 'onClickProductButton' + 'click .product button:not(.start-subscription-button)': 'onClickProductButton' + 'click #close-modal': 'hide' + 'click .start-subscription-button': 'onClickStartSubscription' constructor: (options) -> super(options) + @timestampForPurchase = new Date().getTime() @state = 'standby' if application.isIPadApp @products = [] Backbone.Mediator.publish 'buy-gems-modal:update-products' else @products = @originalProducts + $.post '/db/payment/check-stripe-charges', (something, somethingElse, jqxhr) => + if jqxhr.status is 201 + @state = 'recovered_charge' + @render() getRenderData: -> c = super() c.products = @products c.state = @state + c.stateMessage = @stateMessage return c onIPadProducts: (e) -> @@ -56,16 +65,17 @@ module.exports = class BuyGemsModal extends ModalView Backbone.Mediator.publish 'buy-gems-modal:purchase-initiated', { productID: productID } else - application.tracker?.trackEvent 'Started purchase', {productID:productID}, ['Google Analytics'] + application.tracker?.trackEvent 'Started gem purchase', { productID: productID } stripeHandler.open({ description: $.t(product.i18n) amount: product.amount + bitcoin: true + alipay: if me.get('chinaVersion') or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto' }) @productBeingPurchased = product onStripeReceivedToken: (e) -> - @timestampForPurchase = new Date().getTime() data = { productID: @productBeingPurchased.id stripe: { @@ -77,6 +87,9 @@ module.exports = class BuyGemsModal extends ModalView @render() jqxhr = $.post('/db/payment', data) jqxhr.done(=> + application.tracker?.trackEvent 'Finished gem purchase', + productID: @productBeingPurchased.id + value: @productBeingPurchased.amount document.location.reload() ) jqxhr.fail(=> @@ -89,6 +102,7 @@ module.exports = class BuyGemsModal extends ModalView _.delay f, 2000 else @state = 'unknown_error' + @stateMessage = "#{jqxhr.status}: #{jqxhr.responseText}" @render() ) @@ -100,3 +114,7 @@ module.exports = class BuyGemsModal extends ModalView purchased.gems += product.gems me.set('purchased', purchased) @hide() + + onClickStartSubscription: (e) -> + @openModalView new SubscribeModal() + window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'buy gems modal' diff --git a/app/views/play/modal/LeaderboardModal.coffee b/app/views/play/modal/LeaderboardModal.coffee new file mode 100644 index 000000000..6b0b1bd05 --- /dev/null +++ b/app/views/play/modal/LeaderboardModal.coffee @@ -0,0 +1,60 @@ +ModalView = require 'views/core/ModalView' +template = require 'templates/play/modal/leaderboard-modal' +LeaderboardTabView = require 'views/play/modal/LeaderboardTabView' +Level = require 'models/Level' +utils = require 'core/utils' + +module.exports = class LeaderboardModal extends ModalView + id: 'leaderboard-modal' + template: template + instant: true + timespans: ['day', 'week', 'all'] + + subscriptions: {} + + events: + 'shown.bs.tab #leaderboard-nav a': 'onTabShown' + 'click #close-modal': 'hide' + + constructor: (options) -> + super options + @levelSlug = @options.levelSlug + level = new Level({_id: @levelSlug}) + level.project = ['name', 'i18n', 'scoreType', 'original'] + @level = @supermodel.loadModel(level, 'level').model + + getRenderData: (c) -> + c = super c + c.submenus = [] + for scoreType in @level.get('scoreTypes') ? [] + for timespan in @timespans + c.submenus.push scoreType: scoreType, timespan: timespan + c.levelName = utils.i18n @level.attributes, 'name' + c + + afterRender: -> + super() + return unless @supermodel.finished() + for scoreType, scoreTypeIndex in @level.get('scoreTypes') ? [] + for timespan, timespanIndex in @timespans + submenuView = new LeaderboardTabView scoreType: scoreType, timespan: timespan, level: @level + @insertSubView submenuView, @$el.find "##{scoreType}-#{timespan}-view .leaderboard-tab-view" + if scoreTypeIndex + timespanIndex is 0 + submenuView.$el.parent().addClass 'active' + submenuView.onShown?() + @playSound 'game-menu-open' + @$el.find('.nano:visible').nanoScroller() + + onTabShown: (e) -> + @playSound 'game-menu-tab-switch' + tabChunks = e.target.hash.substring(1).split '-' + scoreType = tabChunks[0 ... tabChunks.length - 2].join '-' + timespan = tabChunks[tabChunks.length - 2] + subview = _.find @subviews, scoreType: scoreType, timespan: timespan + subview.onShown?() + otherSubview.onHidden?() for subviewKey, otherSubview of @subviews when otherSubview isnt subview + + onHidden: -> + super() + subview.onHidden?() for subviewKey, subview of @subviews + @playSound 'game-menu-close' diff --git a/app/views/play/modal/LeaderboardTabView.coffee b/app/views/play/modal/LeaderboardTabView.coffee new file mode 100644 index 000000000..7912af634 --- /dev/null +++ b/app/views/play/modal/LeaderboardTabView.coffee @@ -0,0 +1,75 @@ +CocoView = require 'views/core/CocoView' +template = require 'templates/play/modal/leaderboard-tab-view' +CocoCollection = require 'collections/CocoCollection' +LevelSession = require 'models/LevelSession' + +class TopScoresCollection extends CocoCollection + url: '' + model: LevelSession + + constructor: (@level, @scoreType, @timespan) -> + super() + @url = "/db/level/#{@level.get('original')}/top_scores/#{@scoreType}/#{@timespan}" + +module.exports = class LeaderboardTabView extends CocoView + template: template + className: 'leaderboard-tab-view' + + events: + 'click tbody tr.viewable': 'onClickRow' + + constructor: (options) -> + super options + @level = @options.level + @scoreType = @options.scoreType ? 'time' + @timespan = @options.timespan + + destroy: -> + super() + + getRenderData: -> + c = super() + c.scoreType = @scoreType + c.timespan = @timespan + c.topScores = @formatTopScores() + c.loading = not @sessions or @sessions.loading + c._ = _ + c + + afterRender: -> + super() + + formatTopScores: -> + return [] unless @sessions?.models + rows = [] + for s in @sessions.models + row = {} + score = _.find s.get('state').topScores, type: @scoreType + row.ago = moment(new Date(score.date)).fromNow() + row.score = @formatScore score + row.creatorName = s.get 'creatorName' + row.creator = s.get 'creator' + row.session = s.id + row.codeLanguage = s.get 'codeLanguage' + row.hero = s.get('heroConfig')?.thangType + row.inventory = s.get('heroConfig')?.inventory + rows.push row + rows + + formatScore: (score) -> + switch score.type + when 'time' then -score.score.toFixed(2) + 's' + when 'damage-taken' then -Math.round score.score + when 'damage-dealt', 'gold-collected', 'difficulty' then Math.round score.score + else score.score + + onShown: -> + return if @hasShown + @hasShown = true + topScores = new TopScoresCollection @level, @scoreType, @timespan + @sessions = @supermodel.loadCollection(topScores, 'sessions', {cache: false}, 0).model + + onClickRow: (e) -> + sessionID = $(e.target).closest('tr').data 'session-id' + url = "/play/level/#{@level.get('slug')}?session=#{sessionID}&observing=true" + window.open url, '_blank' diff --git a/app/views/play/modal/PlayAchievementsModal.coffee b/app/views/play/modal/PlayAchievementsModal.coffee index 2601abb2e..3328c315a 100644 --- a/app/views/play/modal/PlayAchievementsModal.coffee +++ b/app/views/play/modal/PlayAchievementsModal.coffee @@ -30,15 +30,16 @@ module.exports = class PlayAchievementsModal extends ModalView 'i18n' 'rewards' 'collection' + 'function' ]) earnedAchievementsFetcher = new CocoCollection([], {url: '/db/earned_achievement', model: EarnedAchievement}) - earnedAchievementsFetcher.setProjection([ 'achievement' ]) + earnedAchievementsFetcher.setProjection ['achievement', 'achievedAmount'] achievementsFetcher.skip = 0 - achievementsFetcher.fetch({data: {skip: 0, limit: PAGE_SIZE}}) + achievementsFetcher.fetch cache: false, data: {skip: 0, limit: PAGE_SIZE} earnedAchievementsFetcher.skip = 0 - earnedAchievementsFetcher.fetch({data: {skip: 0, limit: PAGE_SIZE}}) + earnedAchievementsFetcher.fetch cache: false, data: {skip: 0, limit: PAGE_SIZE} @listenTo achievementsFetcher, 'sync', @onAchievementsLoaded @listenTo earnedAchievementsFetcher, 'sync', @onEarnedAchievementsLoaded @@ -53,7 +54,7 @@ module.exports = class PlayAchievementsModal extends ModalView @achievements.add(fetcher.models) if needMore fetcher.skip += PAGE_SIZE - fetcher.fetch({data: {skip: fetcher.skip, limit: PAGE_SIZE}}) + fetcher.fetch cache: false, data: {skip: fetcher.skip, limit: PAGE_SIZE} else @stopListening(fetcher) @onEverythingLoaded() @@ -64,7 +65,7 @@ module.exports = class PlayAchievementsModal extends ModalView @earnedMap[earned.get('achievement')] = earned if needMore fetcher.skip += PAGE_SIZE - fetcher.fetch({data: {skip: fetcher.skip, limit: PAGE_SIZE}}) + fetcher.fetch cache: false, data: {skip: fetcher.skip, limit: PAGE_SIZE} else @stopListening(fetcher) @onEverythingLoaded() @@ -75,6 +76,9 @@ module.exports = class PlayAchievementsModal extends ModalView if earned = @earnedMap[achievement.id] achievement.earned = earned achievement.earnedDate = earned.getCreationDate() + expFunction = achievement.getExpFunction() + achievement.earnedGems = Math.round (achievement.get('rewards')?.gems or 0) * expFunction earned.get('achievedAmount') + achievement.earnedPoints = Math.round (achievement.get('worth', true) or 0) * expFunction earned.get('achievedAmount') achievement.earnedDate ?= '' @achievements.comparator = (m) -> m.earnedDate @achievements.sort() diff --git a/app/views/play/modal/PlayHeroesModal.coffee b/app/views/play/modal/PlayHeroesModal.coffee index 35cbeb3d4..63c9258f8 100644 --- a/app/views/play/modal/PlayHeroesModal.coffee +++ b/app/views/play/modal/PlayHeroesModal.coffee @@ -7,8 +7,10 @@ SpriteBuilder = require 'lib/sprites/SpriteBuilder' AudioPlayer = require 'lib/AudioPlayer' utils = require 'core/utils' BuyGemsModal = require 'views/play/modal/BuyGemsModal' +AuthModal = require 'views/core/AuthModal' Purchase = require 'models/Purchase' -LevelOptions = require 'lib/LevelOptions' +LayerAdapter = require 'lib/surface/LayerAdapter' +Lank = require 'lib/surface/Lank' module.exports = class PlayHeroesModal extends ModalView className: 'modal fade play-modal' @@ -27,7 +29,7 @@ module.exports = class PlayHeroesModal extends ModalView shortcuts: 'left': -> @$el.find('#hero-carousel').carousel('prev') if @heroes.models.length and not @$el.hasClass 'secret' 'right': -> @$el.find('#hero-carousel').carousel('next') if @heroes.models.length and not @$el.hasClass 'secret' - 'enter': 'saveAndHide' + 'enter': -> @saveAndHide() if @visibleHero and not @visibleHero.locked constructor: (options) -> super options @@ -40,23 +42,24 @@ module.exports = class PlayHeroesModal extends ModalView @listenToOnce @heroes, 'sync', @onHeroesLoaded @supermodel.loadCollection(@heroes, 'heroes') @stages = {} + @layers = [] @session = options.session @initCodeLanguageList options.hadEverChosenHero - @heroAnimationInterval = setInterval @animateHeroes, 2500 + @heroAnimationInterval = setInterval @animateHeroes, 1000 onHeroesLoaded: -> @formatHero hero for hero in @heroes.models formatHero: (hero) -> - hero.name = utils.i18n hero.attributes, 'extendedName' # or whatever the property name ends up being + hero.name = utils.i18n hero.attributes, 'extendedName' hero.name ?= utils.i18n hero.attributes, 'name' hero.description = utils.i18n hero.attributes, 'description' hero.unlockLevelName = utils.i18n hero.attributes, 'unlockLevelName' original = hero.get('original') hero.locked = not me.ownsHero(original) hero.purchasable = hero.locked and (original in (me.get('earned')?.heroes ? [])) - if @options.levelID and allowedHeroSlugs = LevelOptions[@options.levelID]?.allowedHeroes - hero.restricted = not (hero.get('slug') in allowedHeroSlugs) + if @options.level and allowedHeroes = @options.level.get 'allowedHeroes' + hero.restricted = not (hero.get('original') in allowedHeroes) hero.class = (hero.get('heroClass') or 'warrior').toLowerCase() hero.stats = hero.getHeroStats() @@ -68,40 +71,49 @@ module.exports = class PlayHeroesModal extends ModalView context.codeLanguage = @codeLanguage = @options?.session?.get('codeLanguage') ? me.get('aceConfig')?.language ? 'python' context.confirmButtonI18N = @confirmButtonI18N context.visibleHero = @visibleHero + context.gems = me.gems() + context.isIE = @isIE() context afterRender: -> super() return unless @supermodel.finished() + @$el.find('.hero-avatar').addClass 'ie' if @isIE() heroes = @heroes.models @$el.find('.hero-indicator').each -> heroID = $(@).data('hero-id') hero = _.find heroes, (hero) -> hero.get('original') is heroID - $(@).find('.hero-avatar').css('background-image', "url(#{hero.getPortraitURL()})").tooltip() + $(@).find('.hero-avatar').css('background-image', "url(#{hero.getPortraitURL()})").addClass('has-tooltip').tooltip() @canvasWidth = 313 # @$el.find('canvas').width() # unreliable, whatever @canvasHeight = @$el.find('canvas').height() heroConfig = @options?.session?.get('heroConfig') ? me.get('heroConfig') ? {} heroIndex = Math.max 0, _.findIndex(heroes, ((hero) -> hero.get('original') is heroConfig.thangType)) @$el.find(".hero-item:nth-child(#{heroIndex + 1}), .hero-indicator:nth-child(#{heroIndex + 1})").addClass('active') @onHeroChanged direction: null, relatedTarget: @$el.find('.hero-item')[heroIndex] - @$el.find('.hero-stat').tooltip() + @$el.find('.hero-stat').addClass('has-tooltip').tooltip() @buildCodeLanguages() - Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1 rerenderFooter: -> @formatHero @visibleHero @renderSelectors '#hero-footer' @buildCodeLanguages() + @$el.find('#gems-count-container').toggle Boolean @visibleHero.purchasable initCodeLanguageList: (hadEverChosenHero) -> - @codeLanguageList = [ - {id: 'python', name: "Python (#{$.i18n.t('choose_hero.default')})"} - {id: 'javascript', name: 'JavaScript'} - {id: 'coffeescript', name: 'CoffeeScript'} - {id: 'clojure', name: "Clojure (#{$.i18n.t('choose_hero.experimental')})"} - {id: 'lua', name: "Lua (#{$.i18n.t('choose_hero.experimental')})"} - {id: 'io', name: "Io (#{$.i18n.t('choose_hero.experimental')})"} - ] + if application.isIPadApp + @codeLanguageList = [ + {id: 'python', name: "Python (#{$.i18n.t('choose_hero.default')})"} + {id: 'javascript', name: 'JavaScript'} + ] + else + @codeLanguageList = [ + {id: 'python', name: "Python (#{$.i18n.t('choose_hero.default')})"} + {id: 'javascript', name: 'JavaScript'} + {id: 'coffeescript', name: "CoffeeScript (#{$.i18n.t('choose_hero.experimental')})"} + {id: 'clojure', name: "Clojure (#{$.i18n.t('choose_hero.experimental')})"} + {id: 'lua', name: 'Lua'} + {id: 'io', name: "Io (#{$.i18n.t('choose_hero.experimental')})"} + ] onHeroChanged: (e) -> direction = e.direction # 'left' or 'right' @@ -140,43 +152,39 @@ module.exports = class PlayHeroesModal extends ModalView return hero fullHero = @getFullHero hero.get 'original' onLoaded = => - return unless canvas = $(".hero-item[data-hero-id='#{fullHero.get('original')}'] canvas") + canvas = $(".hero-item[data-hero-id='#{fullHero.get('original')}'] canvas") + return unless canvas.length # Don't render it if it's not on the screen. + unless fullHero.get 'raw' + console.error "Couldn't make animation for #{fullHero.get('name')} with attributes #{_.cloneDeep(fullHero.attributes)}. Was it loaded with an improper projection or something?", fullHero + @rerenderFooter() + return canvas.show().prop width: @canvasWidth, height: @canvasHeight - builder = new SpriteBuilder(fullHero) - movieClip = builder.buildMovieClip(fullHero.get('actions').attack?.animation ? fullHero.get('actions').idle.animation) - movieClip.scaleX = movieClip.scaleY = canvas.prop('height') / 120 # Average hero height is ~110px tall at normal resolution - movieClip.regX = -fullHero.get('positions').registration.x - movieClip.regY = -fullHero.get('positions').registration.y - movieClip.x = canvas.prop('width') * 0.5 - movieClip.y = canvas.prop('height') * 0.925 # This is where the feet go. - if fullHero.get('name') is 'Knight' - movieClip.scaleX *= 0.7 - movieClip.scaleY *= 0.7 - if fullHero.get('name') is 'Potion Master' - movieClip.scaleX *= 0.9 - movieClip.scaleY *= 0.9 - movieClip.regX *= 1.1 - movieClip.regY *= 1.4 - if fullHero.get('name') is 'Samurai' - movieClip.scaleX *= 0.7 - movieClip.scaleY *= 0.7 - movieClip.regX *= 1.2 - movieClip.regY *= 1.35 - if fullHero.get('name') is 'Librarian' - movieClip.regX *= 0.7 - movieClip.regY *= 1.2 - if fullHero.get('name') is 'Sorcerer' - movieClip.scaleX *= 0.9 - movieClip.scaleY *= 0.9 - movieClip.regX *= 1.15 - movieClip.regY *= 1.3 - stage = new createjs.Stage(canvas[0]) + layer = new LayerAdapter({webGL:true}) + @layers.push layer + layer.resolutionFactor = 8 # hi res! + layer.buildAsync = false + multiplier = 7 + layer.scaleX = layer.scaleY = multiplier + lank = new Lank(fullHero, {preloadSounds: false}) + + layer.addLank(lank) + layer.on 'new-spritesheet', -> + #- maybe put some more normalization here? + m = multiplier + m *= 0.75 if fullHero.get('slug') in ['knight', 'samurai', 'librarian', 'sorcerer', 'necromancer'] # These heroes are larger for some reason. Shrink 'em. + layer.container.scaleX = layer.container.scaleY = m + layer.container.children[0].x = 160/m + layer.container.children[0].y = 250/m + if fullHero.get('slug') in ['forest-archer', 'librarian', 'sorcerer', 'potion-master', 'necromancer'] + layer.container.children[0].y -= 3 + if fullHero.get('slug') in ['librarian', 'sorcerer', 'potion-master', 'necromancer'] + layer.container.children[0].x -= 3 + + stage = new createjs.SpriteStage(canvas[0]) @stages[heroIndex] = stage - stage.addChild movieClip + stage.addChild layer.container stage.update() - movieClip.loop = false - movieClip.gotoAndPlay 0 unless preloading createjs.Ticker.addEventListener 'tick', stage @playSelectionSound hero @@ -190,7 +198,8 @@ module.exports = class PlayHeroesModal extends ModalView animateHeroes: => return unless @visibleHero heroIndex = Math.max 0, _.findIndex(@heroes.models, ((hero) => hero.get('original') is @visibleHero.get('original'))) - @stages[heroIndex]?.children?[0]?.gotoAndPlay? 0 + animation = _.sample(['attack', 'move_side', 'move_fore']) # Must be in LayerAdapter default actions. + @stages[heroIndex]?.children?[0]?.children?[0]?.gotoAndPlay? animation playSelectionSound: (hero) -> return if @$el.hasClass 'secret' @@ -250,9 +259,12 @@ module.exports = class PlayHeroesModal extends ModalView @$el.one 'click', (e) -> button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0] + askToSignUp: -> + authModal = new AuthModal supermodel: @supermodel + authModal.mode = 'signup' + return @openModalView authModal + askToBuyGems: (unlockButton) -> - if me.getGemPromptGroup() is 'no-prompt' - return @openModalView new BuyGemsModal() @$el.find('.unlock-button').popover 'destroy' popoverTemplate = buyGemsPromptTemplate {} unlockButton.popover( @@ -268,6 +280,7 @@ module.exports = class PlayHeroesModal extends ModalView onBuyGemsPromptButtonClicked: (e) -> @playSound 'menu-button-click' + return @askToSignUp() if me.get('anonymous') @openModalView new BuyGemsModal() onClickedSomewhere: (e) -> @@ -278,14 +291,24 @@ module.exports = class PlayHeroesModal extends ModalView #- Exiting saveAndHide: -> - hero = @selectedHero.get('original') + hero = @selectedHero?.get('original') + hero ?= @visibleHero?.get('original') if @visibleHero?.loaded and not @visibleHero.locked + unless hero + console.error 'Somehow we tried to hide without having a hero selected yet...' + noty { + text: "Error: hero not loaded. If this keeps happening, please report the bug." + layout: 'topCenter' + timeout: 10000 + type: 'error' + } + return if @session changed = @updateHeroConfig(@session, hero) if @session.get('codeLanguage') isnt @codeLanguage @session.set('codeLanguage', @codeLanguage) changed = true - Backbone.Mediator.publish 'tome:change-language', language: @codeLanguage, reload: true + #Backbone.Mediator.publish 'tome:change-language', language: @codeLanguage, reload: true # We'll reload the PlayLevelView instead. @session.patch() if changed @@ -302,6 +325,7 @@ module.exports = class PlayHeroesModal extends ModalView @trigger?('confirm-click', hero: @selectedHero) updateHeroConfig: (model, hero) -> + return false unless hero heroConfig = _.clone(model.get('heroConfig')) or {} if heroConfig.thangType isnt hero heroConfig.thangType = hero @@ -317,4 +341,5 @@ module.exports = class PlayHeroesModal extends ModalView for heroIndex, stage of @stages createjs.Ticker.removeEventListener "tick", stage stage.removeAllChildren() + layer.destroy() for layer in @layers super() diff --git a/app/views/play/modal/PlayItemsModal.coffee b/app/views/play/modal/PlayItemsModal.coffee index 206b94747..0c7ddb878 100644 --- a/app/views/play/modal/PlayItemsModal.coffee +++ b/app/views/play/modal/PlayItemsModal.coffee @@ -3,10 +3,12 @@ template = require 'templates/play/modal/play-items-modal' buyGemsPromptTemplate = require 'templates/play/modal/buy-gems-prompt' ItemDetailsView = require './ItemDetailsView' BuyGemsModal = require 'views/play/modal/BuyGemsModal' +AuthModal = require 'views/core/AuthModal' CocoCollection = require 'collections/CocoCollection' ThangType = require 'models/ThangType' LevelComponent = require 'models/LevelComponent' +Level = require 'models/Level' Purchase = require 'models/Purchase' utils = require 'core/utils' @@ -51,8 +53,11 @@ module.exports = class PlayItemsModal extends ModalView 'click .buy-gems-prompt-button': 'onBuyGemsPromptButtonClicked' 'click #close-modal': 'hide' 'click': 'onClickedSomewhere' + 'update .tab-pane .nano': 'onScrollItemPane' + 'click #hero-type-select label': 'onClickHeroTypeSelect' constructor: (options) -> + @onScrollItemPane = _.throttle(_.bind(@onScrollItemPane, @), 200) super options @items = new Backbone.Collection() @itemCategoryCollections = {} @@ -97,6 +102,10 @@ module.exports = class PlayItemsModal extends ModalView model.comingSoon = not model.getFrontFacingStats().props.length and not _.size(model.getFrontFacingStats().stats) and not model.owned # Temp: while there are placeholder items @idToItem[model.id] = model + if itemFetcher.skip isnt 0 + # Make sure we render the newly fetched items, except the first time (when it happens automatically). + @render() + if needMore itemFetcher.skip += PAGE_SIZE itemFetcher.fetch({data: {skip: itemFetcher.skip, limit: PAGE_SIZE}}) @@ -117,6 +126,10 @@ module.exports = class PlayItemsModal extends ModalView @itemDetailsView = new ItemDetailsView() @insertSubView(@itemDetailsView) @$el.find("a[href='#item-category-armor']").click() # Start on armor tab, if it's there. + earnedLevels = me.get('earned')?.levels or [] + if Level.levels['defense-of-plainswood'] not in earnedLevels + @$el.find('#misc-tab').hide() + @$el.find('#hero-type-select #warrior').click() # Start on warrior tab, if low level. onHidden: -> super() @@ -143,7 +156,28 @@ module.exports = class PlayItemsModal extends ModalView onTabClicked: (e) -> @playSound 'game-menu-tab-switch' - $($(e.target).attr('href')).find('.nano').nanoScroller({alwaysVisible: true}) + nano = $($(e.target).attr('href')).find('.nano') + nano.nanoScroller({alwaysVisible: true}) + @paneNanoContent = nano.find('.nano-content') + @onScrollItemPane() + + onScrollItemPane: -> + # dynamically load visible items when the user scrolls enough to see them + return console.error "Couldn't update scroll, since paneNanoContent wasn't initialized." unless @paneNanoContent + items = @paneNanoContent.find('.item:not(.loaded)') + threshold = @paneNanoContent.height() + 100 + for itemEl in items + itemEl = $(itemEl) + if itemEl.position().top < threshold + $(itemEl).addClass('loaded') + item = @idToItem[itemEl.data('item-id')] + itemEl.find('.item-silhouette, .item-img').attr('src', item.getPortraitURL()) + + onClickHeroTypeSelect: (e) -> + value = $(e.target).closest('label').attr('id') + tabContent = @$el.find('.tab-content') + tabContent.removeClass('filter-wizard filter-ranger filter-warrior') + tabContent.addClass("filter-#{value}") if value isnt 'all' onUnlockButtonClicked: (e) -> e.stopPropagation() @@ -177,14 +211,18 @@ module.exports = class PlayItemsModal extends ModalView @$el.one 'click', (e) -> button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0] + askToSignUp: -> + authModal = new AuthModal supermodel: @supermodel + authModal.mode = 'signup' + return @openModalView authModal + askToBuyGems: (unlockButton) -> - if me.getGemPromptGroup() is 'no-prompt' - return @openModalView new BuyGemsModal() @$el.find('.unlock-button').popover 'destroy' popoverTemplate = buyGemsPromptTemplate {} unlockButton.popover( animation: true trigger: 'manual' + placement: 'top' content: ' ' # template has it container: @$el template: popoverTemplate @@ -194,6 +232,7 @@ module.exports = class PlayItemsModal extends ModalView onBuyGemsPromptButtonClicked: (e) -> @playSound 'menu-button-click' + return @askToSignUp() if me.get('anonymous') @openModalView new BuyGemsModal() onClickedSomewhere: (e) -> diff --git a/app/views/play/modal/PollModal.coffee b/app/views/play/modal/PollModal.coffee new file mode 100644 index 000000000..a4fe1d589 --- /dev/null +++ b/app/views/play/modal/PollModal.coffee @@ -0,0 +1,133 @@ +ModalView = require 'views/core/ModalView' +template = require 'templates/play/modal/poll-modal' +utils = require 'core/utils' +UserPollsRecord = require 'models/UserPollsRecord' + +module.exports = class PollModal extends ModalView + id: 'poll-modal' + template: template + + subscriptions: {} + + events: + 'click #close-modal': 'hide' + 'click .answer:not(.selected)': 'onClickAnswer' + + constructor: (options) -> + super options + @poll = options.poll + @userPollsRecord = options.userPollsRecord + @previousAnswer = (@userPollsRecord.get('polls') ? {})[@poll.id] + @previousReward = (@userPollsRecord.get('rewards') ? {})[@poll.id] + + getRenderData: (c) -> + c = super c + c.poll = @poll + c.i18n = utils.i18n + c.marked = marked + c + + afterRender: -> + super() + @playSound 'game-menu-open' + @updateAnswers() + + onHidden: -> + super() + @playSound 'game-menu-close' + + updateAnswers: (answered) -> + myAnswer = (@userPollsRecord.get('polls') ? {})[@poll.id] + answered = myAnswer? + @$el.find('table, .random-gems-container-wrapper').toggleClass 'answered', answered + return unless answered + @awardRandomGems() + + # Count total votes and find the answer with the most votes. + [maxVotes, totalVotes] = [0, 0] + for answer in @poll.get('answers') or [] + votes = answer.votes or 0 + --votes if answer.key is @previousAnswer + ++votes if answer.key is myAnswer + answer.votes = votes + totalVotes += votes + maxVotes = Math.max maxVotes, votes or 0 + @previousAnswer = myAnswer + @poll.set 'answers', @poll.get('answers') # Update vote count locally (won't save to server). + + # Update each answer cell according to its share of max and total votes. + for answer in @poll.get 'answers' + $answer = @$el.find(".answer[data-answer='#{answer.key}']") + $answer.toggleClass 'selected', answer.key is myAnswer + votes = answer.votes or 0 + votes = maxVotes = totalVotes = 1 unless totalVotes # If no votes yet, just pretend we voted for the first one. + + widthPercentage = (100 * votes / maxVotes) + '%' + votePercentage = Math.round(100 * votes / totalVotes) + '%' + $answer.find('.progress-bar').css('width', '0%').animate({width: widthPercentage}, 'slow') + $answer.find('.vote-percentage').text votePercentage + $answer.find('.vote-count').text votes if me.isAdmin() + + @trigger 'vote-updated' + + onClickAnswer: (e) -> + $selectedAnswer = $(e.target).closest('.answer') + pollVotes = @userPollsRecord.get('polls') ? {} + pollVotes[@poll.id] = $selectedAnswer.data('answer') + @userPollsRecord.set 'polls', pollVotes + @updateAnswers true + @userPollsRecord.save {polls: pollVotes}, {success: => @awardRandomGems?()} + + awardRandomGems: -> + return unless reward = (@userPollsRecord.get('rewards') ? {})[@poll.id] + @$randomNumber = @$el.find('#random-number-comment').empty() + @$randomGems = @$el.find('#random-gems-comment').hide() + @$totalGems = @$el.find('#total-gems-comment').hide() + commentStart = commentStarts[me.get('aceConfig')?.language ? 'python'] + randomNumber = reward.random + randomGems = Math.ceil 2 * randomNumber * reward.level + totalGems = if @previousReward then me.gems() else Math.round me.gems() + randomGems + + if @previousReward + utils.replaceText @$randomNumber.show(), commentStart + randomNumber.toFixed(7) + utils.replaceText @$randomGems.show(), commentStart + randomGems + utils.replaceText @$totalGems.show(), commentStart + totalGems + else + gemNoisesPlayed = 0 + for i in [0 .. 1000] by 25 + do (i) => + @$randomNumber.queue -> + number = if i is 1000 then randomNumber else Math.random() + utils.replaceText $(@), commentStart + number.toFixed(7) + $(@).dequeue() + if Math.random() < randomGems / 40 + gemTrigger = 'gem-' + (gemNoisesPlayed % 4) # 4 gem sounds + ++gemNoisesPlayed + Backbone.Mediator.publish 'audio-player:play-sound', trigger: gemTrigger, volume: 0.475 + i / 2000 + @$randomNumber.delay 25 + @$randomGems.delay(1100).queue -> + utils.replaceText $(@), commentStart + randomGems + $(@).show() + $(@).dequeue() + @$totalGems.delay(1200).queue -> + utils.replaceText $(@), commentStart + totalGems + $(@).show() + $(@).dequeue() + + @previousReward = reward + _.delay (=> + return if @destroyed + earned = me.get('earned') ? {} + earned.gems += randomGems + me.set 'earned', earned + me.trigger 'change:earned' + ), 1200 + + +commentStarts = + javascript: '// ' + python: '# ' + coffeescript: '# ' + clojure: '; ' + lua: '-- ' + io: '// ' diff --git a/app/views/play/modal/ShareProgressModal.coffee b/app/views/play/modal/ShareProgressModal.coffee new file mode 100644 index 000000000..4b3c34943 --- /dev/null +++ b/app/views/play/modal/ShareProgressModal.coffee @@ -0,0 +1,31 @@ +ModalView = require 'views/core/ModalView' +template = require 'templates/play/modal/share-progress-modal' +storage = require 'core/storage' + +module.exports = class SubscribeModal extends ModalView + id: 'share-progress-modal' + template: template + plain: true + closesOnClickOutside: false + + events: + 'click .close-btn': 'hide' + 'click .continue-link': 'hide' + 'click .send-btn': 'onClickSend' + + onClickSend: (e) -> + email = $('.email-input').val() + unless /[\w\.]+@\w+\.\w+/.test email + $('.email-input').parent().addClass('has-error') + $('.email-invalid').show() + return false + + request = @supermodel.addRequestResource 'send_one_time_email', { + url: '/db/user/-/send_one_time_email' + data: {email: email, type: 'share progress modal parent'} + method: 'POST' + }, 0 + request.load() + + storage.save 'sent-parent-email', true + @hide() diff --git a/app/views/user/IdentifyView.coffee b/app/views/user/IdentifyView.coffee new file mode 100644 index 000000000..65d4ab1ae --- /dev/null +++ b/app/views/user/IdentifyView.coffee @@ -0,0 +1,14 @@ +RootView = require 'views/core/RootView' +{me} = require 'core/auth' +template = require 'templates/user/identify-view' + +module.exports = class IdentifyView extends RootView + id: 'identify-view' + template: template + + getRenderData: -> + context = super() + context.callbackID = @getQueryVariable 'id' + context.callbackURL = @getQueryVariable('callback') + "?id=#{context.callbackID}&username=#{me.get('name')}" + context.callbackSource = @getQueryVariable 'source' + context diff --git a/app/views/user/JobProfileCodeModal.coffee b/app/views/user/JobProfileCodeModal.coffee index 1312dd2a1..8ce8839e6 100644 --- a/app/views/user/JobProfileCodeModal.coffee +++ b/app/views/user/JobProfileCodeModal.coffee @@ -1,9 +1,9 @@ ModalView = require 'views/core/ModalView' -template = require 'templates/account/job_profile_code_modal' +template = require 'templates/account/job-profile-code-modal' LevelSessionCodeView = require 'views/common/LevelSessionCodeView' module.exports = class JobProfileCodeModal extends ModalView - id: 'job_profile_code_modal' + id: 'job-profile-code-modal' template: template modalWidthPercent: 90 plain: true diff --git a/app/views/user/JobProfileView.coffee b/app/views/user/JobProfileView.coffee index f67ff3954..9f83db9a3 100644 --- a/app/views/user/JobProfileView.coffee +++ b/app/views/user/JobProfileView.coffee @@ -1,5 +1,5 @@ UserView = require 'views/common/UserView' -template = require 'templates/account/profile' +template = require 'templates/account/job-profile-view' User = require 'models/User' LevelSession = require 'models/LevelSession' CocoCollection = require 'collections/CocoCollection' @@ -10,6 +10,7 @@ UserRemark = require 'models/UserRemark' forms = require 'core/forms' ModelModal = require 'views/modal/ModelModal' JobProfileCodeModal = require './JobProfileCodeModal' +require 'vendor/treema' class LevelSessionsCollection extends CocoCollection url: -> "/db/user/#{@userID}/level.sessions/employer" @@ -84,7 +85,7 @@ module.exports = class JobProfileView extends UserView # Mimicking how the VictoryModal fetches LevelFeedback @remark = new UserRemark() @remark.setURL "/db/user/#{@userID}/remark" - @remark.fetch() + @remark.fetch cache: false @listenToOnce @remark, 'sync', @onRemarkLoaded @listenToOnce @remark, 'error', @onRemarkNotFound diff --git a/app/views/user/MainUserView.coffee b/app/views/user/MainUserView.coffee index e5af3b6cc..658fb3be4 100644 --- a/app/views/user/MainUserView.coffee +++ b/app/views/user/MainUserView.coffee @@ -3,6 +3,7 @@ CocoCollection = require 'collections/CocoCollection' LevelSession = require 'models/LevelSession' template = require 'templates/user/main-user-view' {me} = require 'core/auth' +Clan = require 'models/Clan' EarnedAchievementCollection = require 'collections/EarnedAchievementCollection' class LevelSessionsCollection extends CocoCollection @@ -15,13 +16,16 @@ class LevelSessionsCollection extends CocoCollection module.exports = class MainUserView extends UserView id: 'user-home' template: template - + events: 'click .more-button': 'onClickMoreButton' constructor: (userID, options) -> super options + destroy: -> + @stopListening?() + getRenderData: -> context = super() if @levelSessions and @levelSessions.loaded @@ -47,6 +51,9 @@ module.exports = class MainUserView extends UserView context.favoriteLanguage = favoriteLanguage if @earnedAchievements and @earnedAchievements.loaded context.earnedAchievements = @earnedAchievements + if @clans and @clans.loaded + context.clans = @clans.models + context.idNameMap = @idNameMap context onLoaded: -> @@ -54,11 +61,33 @@ module.exports = class MainUserView extends UserView @supermodel.resetProgress() @levelSessions = new LevelSessionsCollection @user.getSlugOrID() @earnedAchievements = new EarnedAchievementCollection @user.getSlugOrID() - @supermodel.loadCollection @levelSessions, 'levelSessions' - @supermodel.loadCollection @earnedAchievements, 'earnedAchievements' + @supermodel.loadCollection @levelSessions, 'levelSessions', {cache: false} + @supermodel.loadCollection @earnedAchievements, 'earnedAchievements', {cache: false} + sortClanList = (a, b) -> + if a.get('members').length isnt b.get('members').length + if a.get('members').length < b.get('members').length then 1 else -1 + else + b.id.localeCompare(a.id) + @idNameMap = {} + @clans = new CocoCollection([], { url: "/db/user/#{@userID}/clans", model: Clan, comparator: sortClanList }) + @listenTo @clans, 'sync', => + @refreshNameMap @clans?.models + @render?() + @supermodel.loadCollection(@clans, 'clans', {cache: false}) super() + refreshNameMap: (clans) -> + return unless clans? + options = + url: '/db/user/-/names' + method: 'POST' + data: {ids: _.map(clans, (clan) -> clan.get('ownerID'))} + success: (models, response, options) => + @idNameMap[userID] = models[userID].name for userID of models + @render?() + @supermodel.addRequestResource('user_names', options, 0).load() + onClickMoreButton: (e) -> panel = $(e.target).closest('.panel') panel.find('tr.hide').removeClass('hide') - panel.find('.panel-footer').remove() \ No newline at end of file + panel.find('.panel-footer').remove() diff --git a/bin/coco-client-test-runner b/bin/coco-client-test-runner index 6c90569aa..2f5b5dd84 100755 --- a/bin/coco-client-test-runner +++ b/bin/coco-client-test-runner @@ -7,6 +7,6 @@ sleep 5 cd $_scriptDir cd ../ until node_modules/karma/bin/karma start; do - echo "Karma crashed with exit code $?. Respawning.." >&2 + echo "Karma crashed with exit code $?. Respawning..." >&2 sleep 1 done diff --git a/bin/coco-dev-server b/bin/coco-dev-server index e30367130..41052ea26 100755 --- a/bin/coco-dev-server +++ b/bin/coco-dev-server @@ -9,4 +9,3 @@ coco_path = os.getenv("COCO_DIR",os.path.join(current_directory,os.pardir)) nodemon_path = coco_path + os.sep + "node_modules" + os.sep + ".bin" + os.sep + "nodemon" call(nodemon_path + " . --ext \".coffee|.js\" --watch server --watch server_config.js --watch server_setup.coffee --watch app" + os.sep + "schemas",shell=True,cwd=coco_path) - diff --git a/bin/coco-mongodb b/bin/coco-mongodb index eeb3d7aa8..e9e3d78f3 100755 --- a/bin/coco-mongodb +++ b/bin/coco-mongodb @@ -71,7 +71,7 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None): current_directory = os.path.dirname(os.path.realpath(sys.argv[0])) -allowedMongoVersions = ["v2.6.0","v2.6.4"] +allowedMongoVersions = ["v2.6", "v3.0"] if which("mongod") and any(i in subprocess.check_output("mongod --version",shell=True) for i in allowedMongoVersions): mongo_executable = "mongod" else: @@ -92,4 +92,3 @@ mongo_arguments = [mongo_executable + u" --setParameter textSearchEnabled=true - if 'fork' in sys.argv: mongo_arguments[0] += " --fork --logpath ./mongodb.log" call(mongo_arguments,shell=True) - diff --git a/bower.json b/bower.json index e5fb7c3de..30991c9ea 100644 --- a/bower.json +++ b/bower.json @@ -28,14 +28,13 @@ "lodash": "~2.4.1", "backbone": "1.1.0", "jquery-mousewheel": "~3.1.9", - "i18next": "git://github.com/nwinter/i18next.git", + "i18next": "https://github.com/nwinter/i18next.git", "firepad": "~0.1.2", "marked": "~0.3.0", "moment": "~2.5.0", - "aether": "~0.2.39", + "aether": "~0.3.0", "underscore.string": "~2.3.3", "firebase": "~1.0.2", - "catiline": "~2.9.3", "d3": "~3.4.4", "jsondiffpatch": "0.1.8", "nanoscroller": "~0.8.0", @@ -47,7 +46,9 @@ "zatanna": "https://github.com/differentmatt/zatanna.git#master", "modernizr": "~2.8.3", "backfire": "~0.3.0", - "fastclick": "~1.0.3" + "fastclick": "~1.0.3", + "three.js": "~0.71.0", + "lscache": "~1.0.5" }, "overrides": { "backbone": { @@ -72,6 +73,13 @@ "src/formatters/html.css" ] }, + "treema": { + "main": [ + "treema.js", + "treema.css", + "treema-utils.js" + ] + }, "jquery.tablesorter": { "main": [ "js/jquery.tablesorter.js", @@ -90,6 +98,17 @@ }, "modernizr": { "main": "modernizr.js" + }, + "aether": { + "main": [ + "build/aether.js", + "build/clojure.js", + "build/coffeescript.js", + "build/io.js", + "build/javascript.js", + "build/lua.js", + "build/python.js" + ] } }, "devDependencies": { diff --git a/config.coffee b/config.coffee index e83c30899..03d7b4a1e 100644 --- a/config.coffee +++ b/config.coffee @@ -4,52 +4,45 @@ _.str = require 'underscore.string' sysPath = require 'path' fs = require('fs') commonjsHeader = fs.readFileSync('node_modules/brunch/node_modules/commonjs-require-definition/require.js', {encoding: 'utf8'}) +TRAVIS = process.env.COCO_TRAVIS_TEST #- regJoin replace a single '/' with '[\/\\]' so it can handle either forward or backslash -regJoin = (s) -> new RegExp(s.replace(/\//, '[\\\/\\\\]')) - - -#- Find all .coffee and .jade files in /app - -dirStack = ['./app'] -coffeeFiles = [] -jadeFiles = [] - -while dirStack.length - dir = dirStack.pop() - contents = fs.readdirSync(dir) - for file in contents - fullPath = "#{dir}/#{file}" - stat = fs.statSync(fullPath) - if stat.isDirectory() - dirStack.push(fullPath) - else - if _.str.endsWith(file, '.coffee') - coffeeFiles.push(fullPath) - else if _.str.endsWith(file, '.jade') - jadeFiles.push(fullPath) - -console.log "Got #{coffeeFiles.length} coffee files and #{jadeFiles.length} jade files." +regJoin = (s) -> new RegExp(s.replace(/\//g, '[\\\/\\\\]')) #- Build the config exports.config = - + paths: public: 'public' - watched: ['app', 'vendor', 'test/app', 'test/demo'] - + watched: [ + 'app', + 'vendor', + 'test/app', + 'test/demo' + ] + conventions: ignored: (path) -> _.str.startsWith(sysPath.basename(path), '_') - + sourceMaps: 'absoluteUrl' - + overrides: production: sourceMaps: 'absoluteUrl' - + onCompile: (files) -> + # For some reason, production brunch produces two entries, the first of which is wrong: + # //# sourceMappingURL=public/javascripts/app.js.map + # //# sourceMappingURL=/javascripts/app.js.map + # So we remove the ones that have public in them. + exec = require('child_process').exec + exec "perl -pi -e 's/\\/\\/# sourceMappingURL=public.*//g' public/javascripts/*.js" + vagrant: + watcher: + usePolling: true + files: javascripts: defaultExtension: 'coffee' @@ -66,15 +59,18 @@ exports.config = regJoin('^app/core') regJoin('^app/views/core') 'app/locale/locale.coffee' + 'app/locale/en.coffee' 'app/lib/sprites/SpriteBuilder.coffee' # loaded by ThangType ] - + + #- Karma is a bit more tricky to get to work. For now just dump everything into one file so it doesn't need to load anything through ModuleLoader. + 'javascripts/whole-app.js': if TRAVIS then regJoin('^app') else [] + #- Wads. Groups of modules by folder which are loaded as a group when needed. 'javascripts/app/lib.js': regJoin('^app/lib') 'javascripts/app/views/play.js': regJoin('^app/views/play') - 'javascripts/app/views/game-menu.js': regJoin('^app/views/game-menu') 'javascripts/app/views/editor.js': regJoin('^app/views/editor') - + #- world.js, used by the worker to generate the world in game 'javascripts/world.js': [ regJoin('^app/lib/world(?!/test)') @@ -83,14 +79,20 @@ exports.config = regJoin('^vendor/scripts/Box2dWeb-2.1.a.3') regJoin('^vendor/scripts/string_score.js') regJoin('^bower_components/underscore.string') + regJoin('^vendor/scripts/coffeescript.js') ] #- vendor.js, all the vendor libraries 'javascripts/vendor.js': [ - regJoin('^vendor/(?!scripts/Box2d)') - regJoin('^bower_components/(?!aether)') + regJoin('^vendor/scripts/(?!(Box2d|coffeescript|difflib|diffview|jasmine))') + regJoin('^bower_components/(?!(aether|d3|treema|three.js))') + 'bower_components/treema/treema-utils.js' ] - + 'javascripts/whole-vendor.js': if TRAVIS then [ + regJoin('^vendor/scripts/(?!(Box2d|jasmine))') + regJoin('^bower_components/(?!aether)') + ] else [] + #- Other vendor libraries in separate bunches # Include box2dweb for profiling and IE9 @@ -99,13 +101,38 @@ exports.config = 'javascripts/box2d.js': regJoin('^vendor/scripts/Box2dWeb-2.1.a.3') 'javascripts/lodash.js': regJoin('^bower_components/lodash/dist/lodash.js') 'javascripts/aether.js': regJoin('^bower_components/aether/build/aether.js') - + 'javascripts/app/vendor/aether-clojure.js': 'bower_components/aether/build/clojure.js' + 'javascripts/app/vendor/aether-coffeescript.js': 'bower_components/aether/build/coffeescript.js' + 'javascripts/app/vendor/aether-io.js': 'bower_components/aether/build/io.js' + 'javascripts/app/vendor/aether-javascript.js': 'bower_components/aether/build/javascript.js' + 'javascripts/app/vendor/aether-lua.js': 'bower_components/aether/build/lua.js' + 'javascripts/app/vendor/aether-python.js': 'bower_components/aether/build/python.js' + + # Any vendor libraries we don't want the client to load immediately + 'javascripts/app/vendor/d3.js': regJoin('^bower_components/d3') + 'javascripts/app/vendor/coffeescript.js': 'vendor/scripts/coffeescript.js' + 'javascripts/app/vendor/difflib.js': 'vendor/scripts/difflib.js' + 'javascripts/app/vendor/diffview.js': 'vendor/scripts/diffview.js' + 'javascripts/app/vendor/treema.js': 'bower_components/treema/treema.js' + 'javascripts/app/vendor/jasmine-bundle.js': regJoin('^vendor/scripts/jasmine') + 'javascripts/app/vendor/jasmine-mock-ajax.js': 'vendor/scripts/jasmine-mock-ajax.js' + 'javascripts/app/vendor/three.js': 'bower_components/three.js/three.min.js' + #- test, demo libraries - 'javascripts/test-app.js': regJoin('^test/app/') + 'javascripts/app/tests.js': regJoin('^test/app/') 'javascripts/demo-app.js': regJoin('^test/demo/') + #- More output files are generated at the below + order: before: [ + # jasmine-bundle.js ordering + 'vendor/scripts/jasmine.js' + 'vendor/scripts/jasmine-html.js' + 'vendor/scripts/jasmine-boot.js' + 'vendor/scripts/jasmine-mock-ajax.js' + + # vendor.js ordering 'bower_components/jquery/dist/jquery.js' 'bower_components/lodash/dist/lodash.js' 'bower_components/backbone/backbone.js' @@ -126,7 +153,7 @@ exports.config = 'vendor/scripts/async.js' 'vendor/scripts/jquery-ui-1.11.1.js.custom.js' ] - + stylesheets: defaultExtension: 'sass' joinTo: @@ -136,24 +163,15 @@ exports.config = 'app/styles/bootstrap/*' 'vendor/styles/nanoscroller.scss' ] - + templates: defaultExtension: 'jade' joinTo: - 'javascripts/app.js': [ - regJoin('^app/templates/core') - 'app/templates/modal/error.jade' - 'app/templates/not_found.jade' - 'app/templates/achievements/achievement-popup.jade' - 'app/templates/loading.jade' - 'app/templates/loading_error.jade' - 'app/templates/modal/contact.jade' - 'app/templates/modal/modal_base.jade' - 'app/templates/modal/diplomat_suggestion.jade' - ] + 'javascripts/app.js': regJoin('^app/templates/core') 'javascripts/app/views/play.js': regJoin('^app/templates/play') 'javascripts/app/views/game-menu.js': regJoin('^app/templates/game-menu') 'javascripts/app/views/editor.js': regJoin('^app/templates/editor') + 'javascripts/whole-app.js': if TRAVIS then regJoin('^app/templates') else [] framework: 'backbone' @@ -181,22 +199,53 @@ exports.config = sass: mode: 'ruby' allowCache: true + bless: + cacheBuster: false modules: definition: (path, data) -> - needHeaders = [ - 'public/javascripts/app.js' - 'public/javascripts/world.js' - ] - defn = if path in needHeaders then commonjsHeader else '' + needHeaderExpr = regJoin('^public/javascripts/?(app.js|world.js|whole-app.js)') + defn = if path.match(needHeaderExpr) then commonjsHeader else '' return defn +#- Find all .coffee and .jade files in /app + +dirStack = ['./app'] +coffeeFiles = [] +jadeFiles = [] + +while dirStack.length + dir = dirStack.pop() + contents = fs.readdirSync(dir) + for file in contents + fullPath = "#{dir}/#{file}" + stat = fs.statSync(fullPath) + if stat.isDirectory() + dirStack.push(fullPath) + else + if _.str.endsWith(file, '.coffee') + coffeeFiles.push(fullPath) + else if _.str.endsWith(file, '.jade') + jadeFiles.push(fullPath) + for file in coffeeFiles inputFile = file.replace('./app', 'app') outputFile = file.replace('.coffee', '.js').replace('./app', 'javascripts/app') exports.config.files.javascripts.joinTo[outputFile] = inputFile +numBundles = 0 + for file in jadeFiles inputFile = file.replace('./app', 'app') outputFile = file.replace('.jade', '.js').replace('./app', 'javascripts/app') - exports.config.files.templates.joinTo[outputFile] = inputFile + exports.config.files.templates.joinTo[outputFile] = inputFile + + #- If a view template name matches its view, bundle it in there. + templateFileName = outputFile.match(/[^/]+$/)[0] + viewFileName = _.str.capitalize(_.str.camelize(templateFileName)) + possibleViewFilePath = outputFile.replace(templateFileName, viewFileName).replace('/templates/', '/views/') + if exports.config.files.javascripts.joinTo[possibleViewFilePath] + exports.config.files.templates.joinTo[possibleViewFilePath] = inputFile + numBundles += 1 + +console.log "Got #{coffeeFiles.length} coffee files and #{jadeFiles.length} jade files (bundled #{numBundles} of them together)." diff --git a/headless_client.coffee b/headless_client.coffee index 1c426bdba..e2f0f0b33 100644 --- a/headless_client.coffee +++ b/headless_client.coffee @@ -1,5 +1,12 @@ ### This file will simulate games on node.js by emulating the browser environment. +In order to use, followed these steps: +1. Setup dev environment as usual +2. Create a `login.coffee` file in coco which contains: +module.exports = username: 'email@example.com', password: 'your_password' +3. Run `./node_modules/coffee-script/bin/coffee ./headless_client.coffee` +Alternatively, if you wish only to simulate a single game run `coffee ./headless_client.coffee one-game` +Or, if you want to always simulate only one game, change the line below this to "true". This takes way more bandwidth. ### simulateOneGame = false if process.argv[2] is 'one-game' @@ -23,6 +30,7 @@ options = options.heapdump = require('heapdump') if options.heapdump server = if options.testing then 'http://127.0.0.1:3000' else 'https://codecombat.com' +# Use direct instead of live site because jQlone's requests proxy doesn't do caching properly and CloudFlare gets too aggressive. # Disabled modules disable = [ @@ -62,12 +70,16 @@ hookedLoader = (request, parent, isMain) -> if request in disable or ~request.indexOf('templates') console.log 'Ignored ' + request if options.debug return class fake + else if /node_modules[\\\/]aether[\\\/]/.test parent.id + null # Let it through else if '/' in request and not (request[0] is '.') or request is 'application' + #console.log 'making path', path + '/app/' + request, 'from', path, request, 'with parent', parent request = path + '/app/' + request else if request is 'underscore' request = 'lodash' console.log 'loading ' + request if options.debug originalLoader request, parent, isMain + unhook = () -> m._load = originalLoader hook = () -> @@ -90,6 +102,7 @@ GLOBAL.Backbone = require bowerComponentsPath + 'backbone/backbone' unhook() Backbone.$ = $ require bowerComponentsPath + 'validated-backbone-mediator/backbone-mediator' +Backbone.Mediator.setValidationEnabled false GLOBAL.Aether = require 'aether' # Set up new loader. Again. hook() diff --git a/headless_client/jQlone.coffee b/headless_client/jQlone.coffee index 12f806bf1..0d914f094 100644 --- a/headless_client/jQlone.coffee +++ b/headless_client/jQlone.coffee @@ -1,5 +1,5 @@ #jQuery for node, reimplementated for compatibility purposes. Poorly. -#Leaves out all the dome stuff but allows ajax. +#Leaves out all the DOM stuff but allows ajax. _ = require 'lodash' request = require 'request' Deferred = require 'JQDeferred' @@ -20,16 +20,17 @@ $.ajax = (options) -> url = '/' + url unless url[0] is '/' url = $._server + url - data = options.data console.log 'Requesting: ' + JSON.stringify options if $._debug console.log 'URL: ' + url if $._debug + if /db\/thang.type\/names/.test url + url += '?_=' + Math.random() # Make sure that the ThangType names don't get cached, since response varies based on parameters in a way that apparently doesn't work in this hacky implementation. deferred = Deferred() request url: url jar: $._cookies json: options.parse method: options.type - body: data + body: options.data , (error, response, body) -> console.log 'HTTP Request:' + JSON.stringify options if $._debug and not error if responded @@ -48,7 +49,7 @@ $.ajax = (options) -> deferred.promise().done(options.success).fail(options.error) $.extend = (deep, into, from) -> - copy = _.clone(from, deep); + copy = _.clone(from, deep) if into _.assign into, copy copy = into diff --git a/headless_client/test.js b/headless_client/test.js index 3c92b64e0..860ff8c75 100644 --- a/headless_client/test.js +++ b/headless_client/test.js @@ -1,42 +1,41 @@ module.exports = { - "messageGenerated": 1409357317134, + "messageGenerated": 1423774918949, "sessions": [{ - "sessionID": "539bb9f86e1d92310506f138", + "sessionID": "54dd0fc8f927955405a61620", + "submitDate": "2015-02-12T20:56:02.631Z", "team": "humans", "transpiledCode": { - "mak-fod": {}, - "tharin": { - "chooseAction": "var __interceptThis=(function(){var G=this;return function($this,sandbox){if($this==G){return sandbox;}return $this;};})();\nreturn (function (__global) {\n var tmp0, tmp1;\n tmp1 = function () {\n 'use strict'; _aether.logCallStart(_aether._userInfo); var rjust, debug_coin, use_terrify, player, items, tmp93, tmp94, tmp95, tmp96, tmp97, tmp103, tmp104, tmp105, tmp106, tmp107, tmp118, tmp119, tmp120, tmp121, tmp122, tmp123, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129, tmp130, tmp131, tmp132, tmp133, tmp134, tmp135, tmp136, tmp137, tmp138, tmp139, tmp140, tmp141, tmp142, tmp143, tmp144, tmp145, tmp146, tmp147, tmp148, tmp149, tmp150, tmp151, tmp152, tmp153, tmp154, tmp155, tmp156, tmp157, tmp158, tmp159, tmp160, tmp161, tmp162, tmp163, tmp164, tmp165; for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n _aether.logStatementStart([{ofs: 0, row: 0, col: 0}, {ofs: 234, row: 9, col: 2}]); rjust = function (s, n, fill) {\n var num_pad_chars, out, i, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp27, tmp28, tmp29, tmp30, tmp31; s = _aether.createAPIClone(_aether, s); n = _aether.createAPIClone(_aether, n); fill = _aether.createAPIClone(_aether, fill); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n _aether.logStatementStart([{ofs: 43, row: 1, col: 8}, {ofs: 45, row: 1, col: 10}]); tmp2 = ''; _aether.logStatement([{ofs: 43, row: 1, col: 8}, {ofs: 45, row: 1, col: 10}], _aether._userInfo, false);\n tmp6 = 'Math';\n tmp7 = tmp6 in __global;\n if (tmp7) {\n tmp4 = __global[tmp6];\n } else { _aether.logStatementStart([{ofs: 46, row: 1, col: 11}, {ofs: 50, row: 1, col: 15}]);\n tmp8 = 'ReferenceError';\n tmp9 = __global[tmp8];\n tmp10 = new tmp9('ReferenceError: ' + (tmp6 + ' is not defined'));\n throw tmp10;\n _aether.logStatement([{ofs: 46, row: 1, col: 11}, {ofs: 50, row: 1, col: 15}], _aether._userInfo, false); }\n tmp5 = 'floor';\n tmp12 = s;\n _aether.logStatementStart([{ofs: 59, row: 1, col: 24}, {ofs: 61, row: 1, col: 26}]); tmp13 = 10; _aether.logStatement([{ofs: 59, row: 1, col: 24}, {ofs: 61, row: 1, col: 26}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 57, row: 1, col: 22}, {ofs: 61, row: 1, col: 26}]); tmp11 = tmp12 * tmp13; _aether.logStatement([{ofs: 57, row: 1, col: 22}, {ofs: 61, row: 1, col: 26}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 46, row: 1, col: 11}, {ofs: 62, row: 1, col: 27}]); tmp3 = _aether.createAPIClone(_aether, tmp4[tmp5](_aether.restoreAPIClone(_aether, tmp11))); _aether.logStatement([{ofs: 46, row: 1, col: 11}, {ofs: 62, row: 1, col: 27}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 39, row: 1, col: 4}, {ofs: 63, row: 1, col: 28}]); s = tmp2 + tmp3; _aether.logStatement([{ofs: 39, row: 1, col: 4}, {ofs: 63, row: 1, col: 28}], _aether._userInfo, false);\n tmp14 = n;\n tmp16 = s;\n tmp17 = 'length';\n _aether.logStatementStart([{ofs: 92, row: 2, col: 28}, {ofs: 100, row: 2, col: 36}]); tmp15 = tmp16[tmp17]; _aether.logStatement([{ofs: 92, row: 2, col: 28}, {ofs: 100, row: 2, col: 36}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 68, row: 2, col: 4}, {ofs: 101, row: 2, col: 37}]); num_pad_chars = tmp14 - tmp15; _aether.logStatement([{ofs: 68, row: 2, col: 4}, {ofs: 101, row: 2, col: 37}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 106, row: 3, col: 4}, {ofs: 119, row: 3, col: 17}]); out = ''; _aether.logStatement([{ofs: 106, row: 3, col: 4}, {ofs: 119, row: 3, col: 17}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 128, row: 4, col: 8}, {ofs: 137, row: 4, col: 17}]); i = 0; _aether.logStatement([{ofs: 128, row: 4, col: 8}, {ofs: 137, row: 4, col: 17}], _aether._userInfo, false);\n tmp19 = i;\n tmp20 = num_pad_chars;\n _aether.logStatementStart([{ofs: 139, row: 4, col: 19}, {ofs: 156, row: 4, col: 36}]); tmp18 = tmp19 < tmp20; _aether.logStatement([{ofs: 139, row: 4, col: 19}, {ofs: 156, row: 4, col: 36}], _aether._userInfo, false);\n tmp25: {\n while (tmp18) {\n tmp26: {\n tmp27 = out;\n tmp28 = fill;\n _aether.logStatementStart([{ofs: 173, row: 5, col: 8}, {ofs: 190, row: 5, col: 25}]); out = tmp27 + tmp28; _aether.logStatement([{ofs: 173, row: 5, col: 8}, {ofs: 190, row: 5, col: 25}], _aether._userInfo, false);\n }\n tmp23 = i;\n tmp24 = 1;\n i = tmp23 + tmp24;\n tmp21 = i;\n tmp22 = num_pad_chars;\n _aether.logStatementStart([{ofs: 139, row: 4, col: 19}, {ofs: 156, row: 4, col: 36}]); tmp18 = tmp21 < tmp22; _aether.logStatement([{ofs: 139, row: 4, col: 19}, {ofs: 156, row: 4, col: 36}], _aether._userInfo, false);\n }\n }\n tmp29 = out;\n tmp30 = s;\n _aether.logStatementStart([{ofs: 201, row: 7, col: 4}, {ofs: 215, row: 7, col: 18}]); out = tmp29 + tmp30; _aether.logStatement([{ofs: 201, row: 7, col: 4}, {ofs: 215, row: 7, col: 18}], _aether._userInfo, false);\n tmp31 = out;\n return _aether.restoreAPIClone(_aether, tmp31);\n }; _aether.logStatement([{ofs: 0, row: 0, col: 0}, {ofs: 234, row: 9, col: 2}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 236, row: 11, col: 0}, {ofs: 449, row: 15, col: 2}]); debug_coin = function (dist_and_coin) {\n var dist, coin, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, tmp67, tmp68, tmp69, tmp70, tmp71, tmp72, tmp73, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83, tmp84, tmp85, tmp86; dist_and_coin = _aether.createAPIClone(_aether, dist_and_coin); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp32 = dist_and_coin;\n _aether.logStatementStart([{ofs: 308, row: 12, col: 29}, {ofs: 309, row: 12, col: 30}]); tmp33 = 0; _aether.logStatement([{ofs: 308, row: 12, col: 29}, {ofs: 309, row: 12, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 283, row: 12, col: 4}, {ofs: 311, row: 12, col: 32}]); dist = tmp32[tmp33]; _aether.logStatement([{ofs: 283, row: 12, col: 4}, {ofs: 311, row: 12, col: 32}], _aether._userInfo, false);\n tmp34 = dist_and_coin;\n _aether.logStatementStart([{ofs: 341, row: 13, col: 29}, {ofs: 342, row: 13, col: 30}]); tmp35 = 1; _aether.logStatement([{ofs: 341, row: 13, col: 29}, {ofs: 342, row: 13, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 316, row: 13, col: 4}, {ofs: 344, row: 13, col: 32}]); coin = tmp34[tmp35]; _aether.logStatement([{ofs: 316, row: 13, col: 4}, {ofs: 344, row: 13, col: 32}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 358, row: 14, col: 13}]); tmp51 = ''; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 358, row: 14, col: 13}], _aether._userInfo, false);\n tmp55 = 'Math';\n tmp56 = tmp55 in __global;\n if (tmp56) {\n tmp53 = __global[tmp55];\n } else { _aether.logStatementStart([{ofs: 359, row: 14, col: 14}, {ofs: 363, row: 14, col: 18}]);\n tmp57 = 'ReferenceError';\n tmp58 = __global[tmp57];\n tmp59 = new tmp58('ReferenceError: ' + (tmp55 + ' is not defined'));\n throw tmp59;\n _aether.logStatement([{ofs: 359, row: 14, col: 14}, {ofs: 363, row: 14, col: 18}], _aether._userInfo, false); }\n tmp54 = 'floor';\n tmp60 = dist;\n _aether.logStatementStart([{ofs: 359, row: 14, col: 14}, {ofs: 375, row: 14, col: 30}]); tmp52 = _aether.createAPIClone(_aether, tmp53[tmp54](_aether.restoreAPIClone(_aether, tmp60))); _aether.logStatement([{ofs: 359, row: 14, col: 14}, {ofs: 375, row: 14, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 375, row: 14, col: 30}]); tmp49 = tmp51 + tmp52; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 375, row: 14, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 376, row: 14, col: 31}, {ofs: 379, row: 14, col: 34}]); tmp50 = ','; _aether.logStatement([{ofs: 376, row: 14, col: 31}, {ofs: 379, row: 14, col: 34}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 379, row: 14, col: 34}]); tmp47 = tmp49 + tmp50; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 379, row: 14, col: 34}], _aether._userInfo, false);\n tmp63 = 'Math';\n tmp64 = tmp63 in __global;\n if (tmp64) {\n tmp61 = __global[tmp63];\n } else { _aether.logStatementStart([{ofs: 380, row: 14, col: 35}, {ofs: 384, row: 14, col: 39}]);\n tmp65 = 'ReferenceError';\n tmp66 = __global[tmp65];\n tmp67 = new tmp66('ReferenceError: ' + (tmp63 + ' is not defined'));\n throw tmp67;\n _aether.logStatement([{ofs: 380, row: 14, col: 35}, {ofs: 384, row: 14, col: 39}], _aether._userInfo, false); }\n tmp62 = 'floor';\n tmp71 = coin;\n tmp72 = 'pos';\n _aether.logStatementStart([{ofs: 391, row: 14, col: 46}, {ofs: 399, row: 14, col: 54}]); tmp69 = tmp71[tmp72]; _aether.logStatement([{ofs: 391, row: 14, col: 46}, {ofs: 399, row: 14, col: 54}], _aether._userInfo, false);\n tmp70 = 'x';\n _aether.logStatementStart([{ofs: 391, row: 14, col: 46}, {ofs: 401, row: 14, col: 56}]); tmp68 = tmp69[tmp70]; _aether.logStatement([{ofs: 391, row: 14, col: 46}, {ofs: 401, row: 14, col: 56}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 380, row: 14, col: 35}, {ofs: 402, row: 14, col: 57}]); tmp48 = _aether.createAPIClone(_aether, tmp61[tmp62](_aether.restoreAPIClone(_aether, tmp68))); _aether.logStatement([{ofs: 380, row: 14, col: 35}, {ofs: 402, row: 14, col: 57}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 402, row: 14, col: 57}]); tmp45 = tmp47 + tmp48; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 402, row: 14, col: 57}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 403, row: 14, col: 58}, {ofs: 406, row: 14, col: 61}]); tmp46 = ','; _aether.logStatement([{ofs: 403, row: 14, col: 58}, {ofs: 406, row: 14, col: 61}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 406, row: 14, col: 61}]); tmp43 = tmp45 + tmp46; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 406, row: 14, col: 61}], _aether._userInfo, false);\n tmp75 = 'Math';\n tmp76 = tmp75 in __global;\n if (tmp76) {\n tmp73 = __global[tmp75];\n } else { _aether.logStatementStart([{ofs: 407, row: 14, col: 62}, {ofs: 411, row: 14, col: 66}]);\n tmp77 = 'ReferenceError';\n tmp78 = __global[tmp77];\n tmp79 = new tmp78('ReferenceError: ' + (tmp75 + ' is not defined'));\n throw tmp79;\n _aether.logStatement([{ofs: 407, row: 14, col: 62}, {ofs: 411, row: 14, col: 66}], _aether._userInfo, false); }\n tmp74 = 'floor';\n tmp83 = coin;\n tmp84 = 'pos';\n _aether.logStatementStart([{ofs: 418, row: 14, col: 73}, {ofs: 426, row: 14, col: 81}]); tmp81 = tmp83[tmp84]; _aether.logStatement([{ofs: 418, row: 14, col: 73}, {ofs: 426, row: 14, col: 81}], _aether._userInfo, false);\n tmp82 = 'y';\n _aether.logStatementStart([{ofs: 418, row: 14, col: 73}, {ofs: 428, row: 14, col: 83}]); tmp80 = tmp81[tmp82]; _aether.logStatement([{ofs: 418, row: 14, col: 73}, {ofs: 428, row: 14, col: 83}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 407, row: 14, col: 62}, {ofs: 429, row: 14, col: 84}]); tmp44 = _aether.createAPIClone(_aether, tmp73[tmp74](_aether.restoreAPIClone(_aether, tmp80))); _aether.logStatement([{ofs: 407, row: 14, col: 62}, {ofs: 429, row: 14, col: 84}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 429, row: 14, col: 84}]); tmp41 = tmp43 + tmp44; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 429, row: 14, col: 84}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 430, row: 14, col: 85}, {ofs: 433, row: 14, col: 88}]); tmp42 = ','; _aether.logStatement([{ofs: 430, row: 14, col: 85}, {ofs: 433, row: 14, col: 88}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 433, row: 14, col: 88}]); tmp39 = tmp41 + tmp42; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 433, row: 14, col: 88}], _aether._userInfo, false);\n tmp85 = coin;\n tmp86 = 'id';\n _aether.logStatementStart([{ofs: 434, row: 14, col: 89}, {ofs: 441, row: 14, col: 96}]); tmp40 = tmp85[tmp86]; _aether.logStatement([{ofs: 434, row: 14, col: 89}, {ofs: 441, row: 14, col: 96}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 441, row: 14, col: 96}]); tmp37 = tmp39 + tmp40; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 441, row: 14, col: 96}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 442, row: 14, col: 97}, {ofs: 445, row: 14, col: 100}]); tmp38 = '|'; _aether.logStatement([{ofs: 442, row: 14, col: 97}, {ofs: 445, row: 14, col: 100}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 445, row: 14, col: 100}]); tmp36 = tmp37 + tmp38; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 445, row: 14, col: 100}], _aether._userInfo, false);\n return _aether.restoreAPIClone(_aether, tmp36);\n }; _aether.logStatement([{ofs: 236, row: 11, col: 0}, {ofs: 449, row: 15, col: 2}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 451, row: 17, col: 0}, {ofs: 543, row: 20, col: 2}]); use_terrify = function (player) {\n var tmp87, tmp88, tmp89, tmp90, tmp91, tmp92; player = _aether.createAPIClone(_aether, player); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp87 = player;\n tmp88 = 'terrify';\n _aether.logStatementStart([{ofs: 492, row: 18, col: 4}, {ofs: 508, row: 18, col: 20}]); tmp89 = _aether.createAPIClone(_aether, tmp87[tmp88]()); _aether.logStatement([{ofs: 492, row: 18, col: 4}, {ofs: 508, row: 18, col: 20}], _aether._userInfo, false);\n tmp90 = player;\n tmp91 = 'usedTerrify';\n _aether.logStatementStart([{ofs: 514, row: 19, col: 4}, {ofs: 540, row: 19, col: 30}]); tmp92 = true; _aether.logStatement([{ofs: 514, row: 19, col: 4}, {ofs: 540, row: 19, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 514, row: 19, col: 4}, {ofs: 539, row: 19, col: 29}]); tmp90[tmp91] = tmp92; _aether.logStatement([{ofs: 514, row: 19, col: 4}, {ofs: 539, row: 19, col: 29}], _aether._userInfo, false);\n return;\n }; _aether.logStatement([{ofs: 451, row: 17, col: 0}, {ofs: 543, row: 20, col: 2}], _aether._userInfo, false);\n player = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp93 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp94 = 'getItems';\n _aether.logStatementStart([{ofs: 633, row: 25, col: 0}, {ofs: 661, row: 25, col: 28}]); items = _aether.createAPIClone(_aether, tmp93[tmp94]()); _aether.logStatement([{ofs: 633, row: 25, col: 0}, {ofs: 661, row: 25, col: 28}], _aether._userInfo, false);\n tmp95 = items;\n tmp96 = 'filter';\n _aether.logStatementStart([{ofs: 683, row: 26, col: 21}, {ofs: 723, row: 26, col: 61}]); tmp97 = function (x) {\n var tmp98, tmp99, tmp100, tmp101, tmp102; x = _aether.createAPIClone(_aether, x); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp101 = x;\n tmp102 = 'bountyGold';\n _aether.logStatementStart([{ofs: 704, row: 26, col: 42}, {ofs: 716, row: 26, col: 54}]); tmp99 = tmp101[tmp102]; _aether.logStatement([{ofs: 704, row: 26, col: 42}, {ofs: 716, row: 26, col: 54}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 719, row: 26, col: 57}, {ofs: 720, row: 26, col: 58}]); tmp100 = 2; _aether.logStatement([{ofs: 719, row: 26, col: 57}, {ofs: 720, row: 26, col: 58}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 704, row: 26, col: 42}, {ofs: 720, row: 26, col: 58}]); tmp98 = tmp99 > tmp100; _aether.logStatement([{ofs: 704, row: 26, col: 42}, {ofs: 720, row: 26, col: 58}], _aether._userInfo, false);\n return _aether.restoreAPIClone(_aether, tmp98);\n }; _aether.logStatement([{ofs: 683, row: 26, col: 21}, {ofs: 723, row: 26, col: 61}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 662, row: 26, col: 0}, {ofs: 725, row: 26, col: 63}]); items = _aether.createAPIClone(_aether, tmp95[tmp96](_aether.restoreAPIClone(_aether, tmp97))); _aether.logStatement([{ofs: 662, row: 26, col: 0}, {ofs: 725, row: 26, col: 63}], _aether._userInfo, false);\n tmp105 = items;\n tmp106 = 'map';\n _aether.logStatementStart([{ofs: 744, row: 27, col: 18}, {ofs: 806, row: 27, col: 80}]); tmp107 = function (x) {\n var tmp108, tmp109, tmp110, tmp111, tmp112, tmp113, tmp114, tmp115, tmp116, tmp117; x = _aether.createAPIClone(_aether, x); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp111 = rjust;\n tmp115 = player;\n tmp116 = 'distance';\n tmp117 = x;\n _aether.logStatementStart([{ofs: 772, row: 27, col: 46}, {ofs: 790, row: 27, col: 64}]); tmp112 = _aether.createAPIClone(_aether, tmp115[tmp116](_aether.restoreAPIClone(_aether, tmp117))); _aether.logStatement([{ofs: 772, row: 27, col: 46}, {ofs: 790, row: 27, col: 64}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 792, row: 27, col: 66}, {ofs: 793, row: 27, col: 67}]); tmp113 = 7; _aether.logStatement([{ofs: 792, row: 27, col: 66}, {ofs: 793, row: 27, col: 67}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 795, row: 27, col: 69}, {ofs: 798, row: 27, col: 72}]); tmp114 = '0'; _aether.logStatement([{ofs: 795, row: 27, col: 69}, {ofs: 798, row: 27, col: 72}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 766, row: 27, col: 40}, {ofs: 799, row: 27, col: 73}]); tmp109 = _aether.createAPIClone(_aether, tmp111(_aether.restoreAPIClone(_aether, tmp112), _aether.restoreAPIClone(_aether, tmp113), _aether.restoreAPIClone(_aether, tmp114))); _aether.logStatement([{ofs: 766, row: 27, col: 40}, {ofs: 799, row: 27, col: 73}], _aether._userInfo, false);\n tmp110 = x;\n _aether.logStatementStart([{ofs: 765, row: 27, col: 39}, {ofs: 803, row: 27, col: 77}]); tmp108 = [\n tmp109,\n tmp110\n ]; _aether.logStatement([{ofs: 765, row: 27, col: 39}, {ofs: 803, row: 27, col: 77}], _aether._userInfo, false);\n return _aether.restoreAPIClone(_aether, tmp108);\n }; _aether.logStatement([{ofs: 744, row: 27, col: 18}, {ofs: 806, row: 27, col: 80}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 734, row: 27, col: 8}, {ofs: 807, row: 27, col: 81}]); tmp103 = _aether.createAPIClone(_aether, tmp105[tmp106](_aether.restoreAPIClone(_aether, tmp107))); _aether.logStatement([{ofs: 734, row: 27, col: 8}, {ofs: 807, row: 27, col: 81}], _aether._userInfo, false);\n tmp104 = 'sort';\n _aether.logStatementStart([{ofs: 726, row: 27, col: 0}, {ofs: 815, row: 27, col: 89}]); items = _aether.createAPIClone(_aether, tmp103[tmp104]()); _aether.logStatement([{ofs: 726, row: 27, col: 0}, {ofs: 815, row: 27, col: 89}], _aether._userInfo, false);\n tmp118 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp119 = 'say';\n tmp121 = items;\n tmp122 = 'map';\n tmp123 = debug_coin;\n _aether.logStatementStart([{ofs: 825, row: 28, col: 9}, {ofs: 846, row: 28, col: 30}]); tmp120 = _aether.createAPIClone(_aether, tmp121[tmp122](_aether.restoreAPIClone(_aether, tmp123))); _aether.logStatement([{ofs: 825, row: 28, col: 9}, {ofs: 846, row: 28, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 816, row: 28, col: 0}, {ofs: 847, row: 28, col: 31}]); tmp124 = _aether.createAPIClone(_aether, tmp118[tmp119](_aether.restoreAPIClone(_aether, tmp120))); _aether.logStatement([{ofs: 816, row: 28, col: 0}, {ofs: 847, row: 28, col: 31}], _aether._userInfo, false);\n tmp127 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp128 = 'usedTerrify';\n _aether.logStatementStart([{ofs: 854, row: 30, col: 4}, {ofs: 870, row: 30, col: 20}]); tmp126 = tmp127[tmp128]; _aether.logStatement([{ofs: 854, row: 30, col: 4}, {ofs: 870, row: 30, col: 20}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 853, row: 30, col: 3}, {ofs: 870, row: 30, col: 20}]); tmp125 = !tmp126; _aether.logStatement([{ofs: 853, row: 30, col: 3}, {ofs: 870, row: 30, col: 20}], _aether._userInfo, false);\n if (tmp125) {\n tmp132 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp133 = 'distance';\n tmp135 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp136 = 'getNearestEnemy';\n _aether.logStatementStart([{ofs: 895, row: 31, col: 21}, {ofs: 917, row: 31, col: 43}]); tmp134 = _aether.createAPIClone(_aether, tmp135[tmp136]()); _aether.logStatement([{ofs: 895, row: 31, col: 21}, {ofs: 917, row: 31, col: 43}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 881, row: 31, col: 7}, {ofs: 918, row: 31, col: 44}]); tmp130 = _aether.createAPIClone(_aether, tmp132[tmp133](_aether.restoreAPIClone(_aether, tmp134))); _aether.logStatement([{ofs: 881, row: 31, col: 7}, {ofs: 918, row: 31, col: 44}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 921, row: 31, col: 47}, {ofs: 922, row: 31, col: 48}]); tmp131 = 5; _aether.logStatement([{ofs: 921, row: 31, col: 47}, {ofs: 922, row: 31, col: 48}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 881, row: 31, col: 7}, {ofs: 922, row: 31, col: 48}]); tmp129 = tmp130 < tmp131; _aether.logStatement([{ofs: 881, row: 31, col: 7}, {ofs: 922, row: 31, col: 48}], _aether._userInfo, false);\n if (tmp129) {\n tmp138 = use_terrify;\n tmp139 = player;\n _aether.logStatementStart([{ofs: 941, row: 32, col: 15}, {ofs: 960, row: 32, col: 34}]); tmp137 = _aether.createAPIClone(_aether, tmp138(_aether.restoreAPIClone(_aether, tmp139))); _aether.logStatement([{ofs: 941, row: 32, col: 15}, {ofs: 960, row: 32, col: 34}], _aether._userInfo, false);\n _aether.logCallEnd(); return _aether.restoreAPIClone(_aether, tmp137);\n } else {\n ;\n }\n tmp143 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp144 = 'now';\n _aether.logStatementStart([{ofs: 975, row: 34, col: 7}, {ofs: 985, row: 34, col: 17}]); tmp141 = _aether.createAPIClone(_aether, tmp143[tmp144]()); _aether.logStatement([{ofs: 975, row: 34, col: 7}, {ofs: 985, row: 34, col: 17}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 988, row: 34, col: 20}, {ofs: 990, row: 34, col: 22}]); tmp142 = 40; _aether.logStatement([{ofs: 988, row: 34, col: 20}, {ofs: 990, row: 34, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 975, row: 34, col: 7}, {ofs: 990, row: 34, col: 22}]); tmp140 = tmp141 > tmp142; _aether.logStatement([{ofs: 975, row: 34, col: 7}, {ofs: 990, row: 34, col: 22}], _aether._userInfo, false);\n if (tmp140) {\n tmp146 = use_terrify;\n tmp147 = player;\n _aether.logStatementStart([{ofs: 1009, row: 35, col: 15}, {ofs: 1028, row: 35, col: 34}]); tmp145 = _aether.createAPIClone(_aether, tmp146(_aether.restoreAPIClone(_aether, tmp147))); _aether.logStatement([{ofs: 1009, row: 35, col: 15}, {ofs: 1028, row: 35, col: 34}], _aether._userInfo, false);\n _aether.logCallEnd(); return _aether.restoreAPIClone(_aether, tmp145);\n } else {\n ;\n }\n } else {\n ;\n }\n tmp149 = items;\n _aether.logStatementStart([{ofs: 1049, row: 39, col: 10}, {ofs: 1050, row: 39, col: 11}]); tmp150 = 0; _aether.logStatement([{ofs: 1049, row: 39, col: 10}, {ofs: 1050, row: 39, col: 11}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1043, row: 39, col: 4}, {ofs: 1051, row: 39, col: 12}]); tmp148 = tmp149[tmp150]; _aether.logStatement([{ofs: 1043, row: 39, col: 4}, {ofs: 1051, row: 39, col: 12}], _aether._userInfo, false);\n if (tmp148) {\n tmp151 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp152 = 'move';\n tmp158 = items;\n _aether.logStatementStart([{ofs: 1075, row: 40, col: 20}, {ofs: 1076, row: 40, col: 21}]); tmp159 = 0; _aether.logStatement([{ofs: 1075, row: 40, col: 20}, {ofs: 1076, row: 40, col: 21}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1069, row: 40, col: 14}, {ofs: 1077, row: 40, col: 22}]); tmp156 = tmp158[tmp159]; _aether.logStatement([{ofs: 1069, row: 40, col: 14}, {ofs: 1077, row: 40, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1078, row: 40, col: 23}, {ofs: 1079, row: 40, col: 24}]); tmp157 = 1; _aether.logStatement([{ofs: 1078, row: 40, col: 23}, {ofs: 1079, row: 40, col: 24}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1069, row: 40, col: 14}, {ofs: 1080, row: 40, col: 25}]); tmp154 = tmp156[tmp157]; _aether.logStatement([{ofs: 1069, row: 40, col: 14}, {ofs: 1080, row: 40, col: 25}], _aether._userInfo, false);\n tmp155 = 'pos';\n _aether.logStatementStart([{ofs: 1069, row: 40, col: 14}, {ofs: 1084, row: 40, col: 29}]); tmp153 = tmp154[tmp155]; _aether.logStatement([{ofs: 1069, row: 40, col: 14}, {ofs: 1084, row: 40, col: 29}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1059, row: 40, col: 4}, {ofs: 1085, row: 40, col: 30}]); tmp160 = _aether.createAPIClone(_aether, tmp151[tmp152](_aether.restoreAPIClone(_aether, tmp153))); _aether.logStatement([{ofs: 1059, row: 40, col: 4}, {ofs: 1085, row: 40, col: 30}], _aether._userInfo, false);\n } else {\n tmp161 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp162 = 'moveXY';\n _aether.logStatementStart([{ofs: 1112, row: 42, col: 16}, {ofs: 1114, row: 42, col: 18}]); tmp163 = 64; _aether.logStatement([{ofs: 1112, row: 42, col: 16}, {ofs: 1114, row: 42, col: 18}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1116, row: 42, col: 20}, {ofs: 1118, row: 42, col: 22}]); tmp164 = 40; _aether.logStatement([{ofs: 1116, row: 42, col: 20}, {ofs: 1118, row: 42, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1100, row: 42, col: 4}, {ofs: 1119, row: 42, col: 23}]); tmp165 = _aether.createAPIClone(_aether, tmp161[tmp162](_aether.restoreAPIClone(_aether, tmp163), _aether.restoreAPIClone(_aether, tmp164))); _aether.logStatement([{ofs: 1100, row: 42, col: 4}, {ofs: 1119, row: 42, col: 23}], _aether._userInfo, false);\n }\n _aether.logCallEnd(); return;\n };\n tmp0 = 'chooseAction';\n __global[tmp0] = tmp1;\n}(this));" - }, - "coin-generator-9000": {} + "hero-placeholder": { + "plan": "var __interceptThis=(function(){var G=this;return function($this,sandbox){if($this==G){return sandbox;}return $this;};})();\nreturn (function(__global) {\n var tmp0,\n tmp1;\n tmp1 = function() {\n var __argIndexer,\n __gentmp2,\n __resulttmp2,\n _yieldValue,\n self,\n tmp2,\n tmp22,\n tmp3,\n tmp4;\n var $arguments = arguments;\n return $traceurRuntime.generatorWrap(function($ctx) {\n while (true)\n switch ($ctx.state) {\n case 0:\n 'use strict';\n $ctx.state = 20;\n break;\n case 20:\n _aether.logCallStart(_aether._userInfo);\n $ctx.state = 22;\n break;\n case 22:\n ;\n $ctx.state = 24;\n break;\n case 24:\n for (__argIndexer = 0; __argIndexer < $arguments.length; ++__argIndexer)\n $arguments[__argIndexer] = _aether.createAPIClone(_aether, $arguments[__argIndexer]);\n $ctx.state = 26;\n break;\n case 26:\n self = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n $ctx.state = 28;\n break;\n case 28:\n tmp3 = function() {\n var __argIndexer,\n __yieldCount0,\n _yieldValue,\n enemy,\n tmp10,\n tmp11,\n tmp12,\n tmp13,\n tmp14,\n tmp15,\n tmp16,\n tmp17,\n tmp18,\n tmp19,\n tmp20,\n tmp21,\n tmp5,\n tmp8,\n tmp9;\n var $arguments = arguments;\n return $traceurRuntime.generatorWrap(function($ctx) {\n while (true)\n switch ($ctx.state) {\n case 0:\n ;\n $ctx.state = 129;\n break;\n case 129:\n for (__argIndexer = 0; __argIndexer < $arguments.length; ++__argIndexer)\n $arguments[__argIndexer] = _aether.createAPIClone(_aether, $arguments[__argIndexer]);\n $ctx.state = 131;\n break;\n case 131:\n _aether.logStatementStart([{\n ofs: 42,\n row: 2,\n col: 6\n }, {\n ofs: 46,\n row: 2,\n col: 10\n }]);\n $ctx.state = 133;\n break;\n case 133:\n tmp5 = true;\n $ctx.state = 135;\n break;\n case 135:\n _aether.logStatement([{\n ofs: 42,\n row: 2,\n col: 6\n }, {\n ofs: 46,\n row: 2,\n col: 10\n }], _aether._userInfo, false);\n $ctx.state = 137;\n break;\n case 137:\n if (tmp5) {\n $ctx.state = 115;\n break;\n } else {\n $ctx.state = 3;\n break;\n }\n case 115:\n __yieldCount0 = 0;\n $ctx.state = 116;\n break;\n case 116:\n tmp8 = self;\n $ctx.state = 85;\n break;\n case 85:\n tmp9 = 'findNearestEnemy';\n $ctx.state = 87;\n break;\n case 87:\n _aether.logStatementStart([{\n ofs: 157,\n row: 5,\n col: 4\n }, {\n ofs: 162,\n row: 5,\n col: 9\n }]);\n $ctx.state = 89;\n break;\n case 89:\n enemy = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp8[tmp9]()));\n $ctx.state = 91;\n break;\n case 91:\n _aether.logStatement([{\n ofs: 157,\n row: 5,\n col: 4\n }, {\n ofs: 162,\n row: 5,\n col: 9\n }], _aether._userInfo, false);\n $ctx.state = 93;\n break;\n case 93:\n if (_aether._shouldYield) {\n $ctx.state = 11;\n break;\n } else {\n $ctx.state = 18;\n break;\n }\n case 11:\n _yieldValue = _aether._shouldYield;\n $ctx.state = 12;\n break;\n case 12:\n _aether._shouldYield = false;\n $ctx.state = 14;\n break;\n case 14:\n if (this.onAetherYield) {\n this.onAetherYield(_yieldValue);\n }\n $ctx.state = 16;\n break;\n case 16:\n $ctx.state = 8;\n return _yieldValue;\n case 8:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 10;\n break;\n case 10:\n if (typeof __yieldCount0 !== 'undefined' && __yieldCount0 !== null) {\n __yieldCount0++;\n }\n $ctx.state = 18;\n break;\n case 18:\n tmp11 = self;\n $ctx.state = 95;\n break;\n case 95:\n tmp12 = 'isReady';\n $ctx.state = 97;\n break;\n case 97:\n _aether.logStatementStart([{\n ofs: 214,\n row: 7,\n col: 20\n }, {\n ofs: 222,\n row: 7,\n col: 28\n }]);\n $ctx.state = 99;\n break;\n case 99:\n tmp13 = 'cleave';\n $ctx.state = 101;\n break;\n case 101:\n _aether.logStatement([{\n ofs: 214,\n row: 7,\n col: 20\n }, {\n ofs: 222,\n row: 7,\n col: 28\n }], _aether._userInfo, false);\n $ctx.state = 103;\n break;\n case 103:\n _aether.logStatementStart([{\n ofs: 201,\n row: 7,\n col: 7\n }, {\n ofs: 223,\n row: 7,\n col: 29\n }]);\n $ctx.state = 105;\n break;\n case 105:\n tmp10 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp11[tmp12](_aether.restoreAPIClone(_aether, tmp13))));\n $ctx.state = 107;\n break;\n case 107:\n _aether.logStatement([{\n ofs: 201,\n row: 7,\n col: 7\n }, {\n ofs: 223,\n row: 7,\n col: 29\n }], _aether._userInfo, false);\n $ctx.state = 109;\n break;\n case 109:\n if (_aether._shouldYield) {\n $ctx.state = 24;\n break;\n } else {\n $ctx.state = 31;\n break;\n }\n case 24:\n _yieldValue = _aether._shouldYield;\n $ctx.state = 25;\n break;\n case 25:\n _aether._shouldYield = false;\n $ctx.state = 27;\n break;\n case 27:\n if (this.onAetherYield) {\n this.onAetherYield(_yieldValue);\n }\n $ctx.state = 29;\n break;\n case 29:\n $ctx.state = 21;\n return _yieldValue;\n case 21:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 23;\n break;\n case 23:\n if (typeof __yieldCount0 !== 'undefined' && __yieldCount0 !== null) {\n __yieldCount0++;\n }\n $ctx.state = 31;\n break;\n case 31:\n if (tmp10) {\n $ctx.state = 46;\n break;\n } else {\n $ctx.state = 71;\n break;\n }\n case 46:\n tmp14 = self;\n $ctx.state = 47;\n break;\n case 47:\n tmp15 = 'cleave';\n $ctx.state = 49;\n break;\n case 49:\n tmp16 = enemy;\n $ctx.state = 51;\n break;\n case 51:\n _aether.logStatementStart([{\n ofs: 242,\n row: 9,\n col: 8\n }, {\n ofs: 260,\n row: 9,\n col: 26\n }]);\n $ctx.state = 53;\n break;\n case 53:\n tmp17 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp14[tmp15](_aether.restoreAPIClone(_aether, tmp16))));\n $ctx.state = 55;\n break;\n case 55:\n _aether.logStatement([{\n ofs: 242,\n row: 9,\n col: 8\n }, {\n ofs: 260,\n row: 9,\n col: 26\n }], _aether._userInfo, false);\n $ctx.state = 57;\n break;\n case 57:\n if (_aether._shouldYield) {\n $ctx.state = 37;\n break;\n } else {\n $ctx.state = 6;\n break;\n }\n case 37:\n _yieldValue = _aether._shouldYield;\n $ctx.state = 38;\n break;\n case 38:\n _aether._shouldYield = false;\n $ctx.state = 40;\n break;\n case 40:\n if (this.onAetherYield) {\n this.onAetherYield(_yieldValue);\n }\n $ctx.state = 42;\n break;\n case 42:\n $ctx.state = 34;\n return _yieldValue;\n case 34:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 36;\n break;\n case 36:\n if (typeof __yieldCount0 !== 'undefined' && __yieldCount0 !== null) {\n __yieldCount0++;\n }\n $ctx.state = 6;\n break;\n case 71:\n tmp18 = self;\n $ctx.state = 72;\n break;\n case 72:\n tmp19 = 'attack';\n $ctx.state = 74;\n break;\n case 74:\n tmp20 = enemy;\n $ctx.state = 76;\n break;\n case 76:\n _aether.logStatementStart([{\n ofs: 280,\n row: 12,\n col: 8\n }, {\n ofs: 298,\n row: 12,\n col: 26\n }]);\n $ctx.state = 78;\n break;\n case 78:\n tmp21 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp18[tmp19](_aether.restoreAPIClone(_aether, tmp20))));\n $ctx.state = 80;\n break;\n case 80:\n _aether.logStatement([{\n ofs: 280,\n row: 12,\n col: 8\n }, {\n ofs: 298,\n row: 12,\n col: 26\n }], _aether._userInfo, false);\n $ctx.state = 82;\n break;\n case 82:\n if (_aether._shouldYield) {\n $ctx.state = 62;\n break;\n } else {\n $ctx.state = 6;\n break;\n }\n case 62:\n _yieldValue = _aether._shouldYield;\n $ctx.state = 63;\n break;\n case 63:\n _aether._shouldYield = false;\n $ctx.state = 65;\n break;\n case 65:\n if (this.onAetherYield) {\n this.onAetherYield(_yieldValue);\n }\n $ctx.state = 67;\n break;\n case 67:\n $ctx.state = 59;\n return _yieldValue;\n case 59:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 61;\n break;\n case 61:\n if (typeof __yieldCount0 !== 'undefined' && __yieldCount0 !== null) {\n __yieldCount0++;\n }\n $ctx.state = 6;\n break;\n case 6:\n _aether.logStatementStart([{\n ofs: 42,\n row: 2,\n col: 6\n }, {\n ofs: 46,\n row: 2,\n col: 10\n }]);\n $ctx.state = 118;\n break;\n case 118:\n tmp5 = true;\n $ctx.state = 120;\n break;\n case 120:\n _aether.logStatement([{\n ofs: 42,\n row: 2,\n col: 6\n }, {\n ofs: 46,\n row: 2,\n col: 10\n }], _aether._userInfo, false);\n $ctx.state = 122;\n break;\n case 122:\n if (this.onAetherYield) {\n this.onAetherYield('simple loop');\n }\n $ctx.state = 124;\n break;\n case 124:\n if (__yieldCount0 === 0) {\n $ctx.state = 110;\n break;\n } else {\n $ctx.state = 137;\n break;\n }\n case 110:\n $ctx.state = 111;\n return 'simple loop...';\n case 111:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 137;\n break;\n case 3:\n $ctx.state = -2;\n break;\n case -2:\n return $ctx;\n case -3:\n throw $ctx.storedException;\n default:\n throw 'traceur compiler bug: invalid state in state machine: ' + $ctx.state;\n }\n }, this);\n };\n $ctx.state = 30;\n break;\n case 30:\n tmp4 = 'call';\n $ctx.state = 32;\n break;\n case 32:\n tmp22 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n $ctx.state = 34;\n break;\n case 34:\n __gentmp2 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp3[tmp4](_aether.restoreAPIClone(_aether, tmp22))));\n $ctx.state = 36;\n break;\n case 36:\n if (true) {\n $ctx.state = 5;\n break;\n } else {\n $ctx.state = 16;\n break;\n }\n case 5:\n __resulttmp2 = __gentmp2.next();\n $ctx.state = 6;\n break;\n case 6:\n if (__resulttmp2.done) {\n $ctx.state = 9;\n break;\n } else {\n $ctx.state = 8;\n break;\n }\n case 9:\n tmp2 = __resulttmp2.value;\n $ctx.state = 16;\n break;\n case 8:\n _yieldValue = __resulttmp2.value;\n $ctx.state = 13;\n break;\n case 13:\n if (this.onAetherYield) {\n this.onAetherYield(_yieldValue);\n }\n $ctx.state = 15;\n break;\n case 15:\n $ctx.state = 2;\n return _yieldValue;\n case 2:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 36;\n break;\n case 16:\n ;\n $ctx.state = 38;\n break;\n case 38:\n _aether.logCallEnd();\n $ctx.state = 40;\n break;\n case 40:\n $ctx.returnValue = _aether.restoreAPIClone(_aether, tmp2);\n $ctx.state = -2;\n break;\n case -2:\n return $ctx;\n case -3:\n throw $ctx.storedException;\n default:\n throw 'traceur compiler bug: invalid state in state machine: ' + $ctx.state;\n }\n }, this);\n };\n tmp0 = 'plan';\n __global[tmp0] = tmp1;\n}(this));\n" + } }, - "submittedCodeLanguage": "javascript", + "submittedCodeLanguage": "python", "teamSpells": { - "common": ["coin-generator-9000/chooseAction"], - "humans": ["tharin/chooseAction"], - "ogres": ["mak-fod/chooseAction"] + "humans": ["hero-placeholder/plan"], + "ogres": ["hero-placeholder-1/plan"] }, - "levelID": "gold-rush", - "creatorName": "s-krackas", - "creator": "539b4c9cb4c4f93805452aba", - "totalScore": 36.389248829587835 + "levelID": "dueling-grounds", + "creator": "54d520a09136715505d15297", + "totalScore": 40.47590719392207 }, { - "sessionID": "53fdc33abfe0d7f308a6906a", + "sessionID": "548881e93ab3c0ba091631b0", + "submitDate": "2014-12-10T17:27:26.031Z", "team": "ogres", "transpiledCode": { - "mak-fod": { - "chooseAction": "var __interceptThis=(function(){var G=this;return function($this,sandbox){if($this==G){return sandbox;}return $this;};})();\nreturn (function (__global) {\n var tmp0, tmp1;\n tmp1 = function () {\n 'use strict'; _aether.logCallStart(_aether._userInfo); var items, saystring, currdistance, currindex, i, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64; for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp2 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp3 = 'getItems';\n _aether.logStatementStart([{ofs: 173, row: 4, col: 0}, {ofs: 201, row: 4, col: 28}]); items = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp2[tmp3]())); _aether.logStatement([{ofs: 173, row: 4, col: 0}, {ofs: 201, row: 4, col: 28}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 202, row: 5, col: 0}, {ofs: 221, row: 5, col: 19}]); saystring = ''; _aether.logStatement([{ofs: 202, row: 5, col: 0}, {ofs: 221, row: 5, col: 19}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 242, row: 6, col: 20}, {ofs: 243, row: 6, col: 21}]); tmp4 = 1; _aether.logStatement([{ofs: 242, row: 6, col: 20}, {ofs: 243, row: 6, col: 21}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 222, row: 6, col: 0}, {ofs: 244, row: 6, col: 22}]); currdistance = -tmp4; _aether.logStatement([{ofs: 222, row: 6, col: 0}, {ofs: 244, row: 6, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 245, row: 7, col: 0}, {ofs: 263, row: 7, col: 18}]); currindex = 0; _aether.logStatement([{ofs: 245, row: 7, col: 0}, {ofs: 263, row: 7, col: 18}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 268, row: 8, col: 4}, {ofs: 277, row: 8, col: 13}]); i = 0; _aether.logStatement([{ofs: 268, row: 8, col: 4}, {ofs: 277, row: 8, col: 13}], _aether._userInfo, false);\n tmp6 = i;\n tmp8 = items;\n tmp9 = 'length';\n _aether.logStatementStart([{ofs: 283, row: 8, col: 19}, {ofs: 295, row: 8, col: 31}]); tmp7 = tmp8[tmp9]; _aether.logStatement([{ofs: 283, row: 8, col: 19}, {ofs: 295, row: 8, col: 31}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 279, row: 8, col: 15}, {ofs: 295, row: 8, col: 31}]); tmp5 = tmp6 < tmp7; _aether.logStatement([{ofs: 279, row: 8, col: 15}, {ofs: 295, row: 8, col: 31}], _aether._userInfo, false);\n tmp16: {\n while (tmp5) {\n tmp17: {\n tmp19 = i;\n _aether.logStatementStart([{ofs: 325, row: 9, col: 21}, {ofs: 327, row: 9, col: 23}]); tmp20 = ''; _aether.logStatement([{ofs: 325, row: 9, col: 21}, {ofs: 327, row: 9, col: 23}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 308, row: 9, col: 4}, {ofs: 328, row: 9, col: 24}]); tmp18 = tmp19 + tmp20; _aether.logStatement([{ofs: 308, row: 9, col: 4}, {ofs: 328, row: 9, col: 24}], _aether._userInfo, false);\n tmp21 = saystring;\n tmp22 = tmp18;\n saystring = tmp21 + tmp22;\n tmp24 = currdistance;\n _aether.logStatementStart([{ofs: 353, row: 10, col: 24}, {ofs: 354, row: 10, col: 25}]); tmp26 = 1; _aether.logStatement([{ofs: 353, row: 10, col: 24}, {ofs: 354, row: 10, col: 25}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 352, row: 10, col: 23}, {ofs: 354, row: 10, col: 25}]); tmp25 = -tmp26; _aether.logStatement([{ofs: 352, row: 10, col: 23}, {ofs: 354, row: 10, col: 25}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 336, row: 10, col: 7}, {ofs: 354, row: 10, col: 25}]); tmp23 = tmp24 == tmp25; _aether.logStatement([{ofs: 336, row: 10, col: 7}, {ofs: 354, row: 10, col: 25}], _aether._userInfo, false);\n if (tmp23) {\n tmp27 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp28 = 'distance';\n tmp30 = items;\n tmp31 = i;\n _aether.logStatementStart([{ofs: 395, row: 11, col: 37}, {ofs: 403, row: 11, col: 45}]); tmp29 = tmp30[tmp31]; _aether.logStatement([{ofs: 395, row: 11, col: 37}, {ofs: 403, row: 11, col: 45}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 366, row: 11, col: 8}, {ofs: 405, row: 11, col: 47}]); currdistance = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp27[tmp28](_aether.restoreAPIClone(_aether, tmp29)))); _aether.logStatement([{ofs: 366, row: 11, col: 8}, {ofs: 405, row: 11, col: 47}], _aether._userInfo, false);\n } else {\n ;\n }\n tmp35 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp36 = 'distance';\n tmp38 = items;\n tmp39 = i;\n _aether.logStatementStart([{ofs: 433, row: 13, col: 21}, {ofs: 441, row: 13, col: 29}]); tmp37 = tmp38[tmp39]; _aether.logStatement([{ofs: 433, row: 13, col: 21}, {ofs: 441, row: 13, col: 29}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 419, row: 13, col: 7}, {ofs: 442, row: 13, col: 30}]); tmp33 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp35[tmp36](_aether.restoreAPIClone(_aether, tmp37)))); _aether.logStatement([{ofs: 419, row: 13, col: 7}, {ofs: 442, row: 13, col: 30}], _aether._userInfo, false);\n tmp34 = currdistance;\n _aether.logStatementStart([{ofs: 419, row: 13, col: 7}, {ofs: 457, row: 13, col: 45}]); tmp32 = tmp33 < tmp34; _aether.logStatement([{ofs: 419, row: 13, col: 7}, {ofs: 457, row: 13, col: 45}], _aether._userInfo, false);\n if (tmp32) {\n tmp40 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp41 = 'distance';\n tmp43 = items;\n tmp44 = i;\n _aether.logStatementStart([{ofs: 498, row: 14, col: 37}, {ofs: 506, row: 14, col: 45}]); tmp42 = tmp43[tmp44]; _aether.logStatement([{ofs: 498, row: 14, col: 37}, {ofs: 506, row: 14, col: 45}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 469, row: 14, col: 8}, {ofs: 508, row: 14, col: 47}]); currdistance = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp40[tmp41](_aether.restoreAPIClone(_aether, tmp42)))); _aether.logStatement([{ofs: 469, row: 14, col: 8}, {ofs: 508, row: 14, col: 47}], _aether._userInfo, false);\n currindex = i;\n } else {\n ;\n }\n }\n tmp14 = i;\n tmp15 = 1;\n i = tmp14 + tmp15;\n tmp10 = i;\n tmp12 = items;\n tmp13 = 'length';\n _aether.logStatementStart([{ofs: 283, row: 8, col: 19}, {ofs: 295, row: 8, col: 31}]); tmp11 = tmp12[tmp13]; _aether.logStatement([{ofs: 283, row: 8, col: 19}, {ofs: 295, row: 8, col: 31}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 279, row: 8, col: 15}, {ofs: 295, row: 8, col: 31}]); tmp5 = tmp10 < tmp11; _aether.logStatement([{ofs: 279, row: 8, col: 15}, {ofs: 295, row: 8, col: 31}], _aether._userInfo, false);\n }\n }\n tmp45 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp46 = 'say';\n tmp47 = currindex;\n _aether.logStatementStart([{ofs: 546, row: 20, col: 0}, {ofs: 565, row: 20, col: 19}]); tmp48 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp45[tmp46](_aether.restoreAPIClone(_aether, tmp47)))); _aether.logStatement([{ofs: 546, row: 20, col: 0}, {ofs: 565, row: 20, col: 19}], _aether._userInfo, false);\n tmp50 = items;\n tmp51 = currindex;\n _aether.logStatementStart([{ofs: 571, row: 21, col: 4}, {ofs: 587, row: 21, col: 20}]); tmp49 = tmp50[tmp51]; _aether.logStatement([{ofs: 571, row: 21, col: 4}, {ofs: 587, row: 21, col: 20}], _aether._userInfo, false);\n if (tmp49) {\n tmp52 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp53 = 'move';\n tmp57 = items;\n tmp58 = currindex;\n _aether.logStatementStart([{ofs: 605, row: 22, col: 14}, {ofs: 621, row: 22, col: 30}]); tmp55 = tmp57[tmp58]; _aether.logStatement([{ofs: 605, row: 22, col: 14}, {ofs: 621, row: 22, col: 30}], _aether._userInfo, false);\n tmp56 = 'pos';\n _aether.logStatementStart([{ofs: 605, row: 22, col: 14}, {ofs: 625, row: 22, col: 34}]); tmp54 = tmp55[tmp56]; _aether.logStatement([{ofs: 605, row: 22, col: 14}, {ofs: 625, row: 22, col: 34}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 595, row: 22, col: 4}, {ofs: 626, row: 22, col: 35}]); tmp59 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp52[tmp53](_aether.restoreAPIClone(_aether, tmp54)))); _aether.logStatement([{ofs: 595, row: 22, col: 4}, {ofs: 626, row: 22, col: 35}], _aether._userInfo, false);\n } else {\n tmp60 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp61 = 'moveXY';\n _aether.logStatementStart([{ofs: 653, row: 24, col: 16}, {ofs: 655, row: 24, col: 18}]); tmp62 = 18; _aether.logStatement([{ofs: 653, row: 24, col: 16}, {ofs: 655, row: 24, col: 18}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 657, row: 24, col: 20}, {ofs: 659, row: 24, col: 22}]); tmp63 = 36; _aether.logStatement([{ofs: 657, row: 24, col: 20}, {ofs: 659, row: 24, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 641, row: 24, col: 4}, {ofs: 660, row: 24, col: 23}]); tmp64 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp60[tmp61](_aether.restoreAPIClone(_aether, tmp62), _aether.restoreAPIClone(_aether, tmp63)))); _aether.logStatement([{ofs: 641, row: 24, col: 4}, {ofs: 660, row: 24, col: 23}], _aether._userInfo, false);\n }\n _aether.logCallEnd(); return;\n };\n tmp0 = 'chooseAction';\n __global[tmp0] = tmp1;\n}(this));" + "hero-placeholder-1": { + "plan": "var __interceptThis=(function(){var G=this;return function($this,sandbox){if($this==G){return sandbox;}return $this;};})();\nreturn (function(__global) {\n var tmp0,\n tmp1;\n tmp1 = function() {\n var __argIndexer,\n __yieldCount0,\n _yieldValue,\n tmp10,\n tmp11,\n tmp12,\n tmp13,\n tmp14,\n tmp15,\n tmp16,\n tmp17,\n tmp18,\n tmp19,\n tmp2,\n tmp20,\n tmp21,\n tmp22,\n tmp23,\n tmp24,\n tmp25,\n tmp26,\n tmp27,\n tmp28,\n tmp29,\n tmp30,\n tmp31,\n tmp32,\n tmp33,\n tmp34,\n tmp35,\n tmp36,\n tmp5,\n tmp6,\n tmp7,\n tmp8,\n tmp9;\n var $arguments = arguments;\n return $traceurRuntime.generatorWrap(function($ctx) {\n while (true)\n switch ($ctx.state) {\n case 0:\n 'use strict';\n $ctx.state = 158;\n break;\n case 158:\n _aether.logCallStart(_aether._userInfo);\n $ctx.state = 160;\n break;\n case 160:\n ;\n $ctx.state = 162;\n break;\n case 162:\n for (__argIndexer = 0; __argIndexer < $arguments.length; ++__argIndexer)\n $arguments[__argIndexer] = _aether.createAPIClone(_aether, $arguments[__argIndexer]);\n $ctx.state = 164;\n break;\n case 164:\n _aether.logStatementStart([{\n ofs: 44,\n row: 2,\n col: 7\n }, {\n ofs: 48,\n row: 2,\n col: 11\n }]);\n $ctx.state = 166;\n break;\n case 166:\n tmp2 = true;\n $ctx.state = 168;\n break;\n case 168:\n _aether.logStatement([{\n ofs: 44,\n row: 2,\n col: 7\n }, {\n ofs: 48,\n row: 2,\n col: 11\n }], _aether._userInfo, false);\n $ctx.state = 170;\n break;\n case 170:\n if (tmp2) {\n $ctx.state = 144;\n break;\n } else {\n $ctx.state = 3;\n break;\n }\n case 144:\n __yieldCount0 = 0;\n $ctx.state = 145;\n break;\n case 145:\n tmp7 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n $ctx.state = 112;\n break;\n case 112:\n tmp8 = 'findNearestEnemy';\n $ctx.state = 114;\n break;\n case 114:\n _aether.logStatementStart([{\n ofs: 152,\n row: 5,\n col: 4\n }, {\n ofs: 184,\n row: 5,\n col: 36\n }]);\n $ctx.state = 116;\n break;\n case 116:\n tmp6 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp7[tmp8]()));\n $ctx.state = 118;\n break;\n case 118:\n _aether.logStatement([{\n ofs: 152,\n row: 5,\n col: 4\n }, {\n ofs: 184,\n row: 5,\n col: 36\n }], _aether._userInfo, false);\n $ctx.state = 120;\n break;\n case 120:\n if (_aether._shouldYield) {\n $ctx.state = 11;\n break;\n } else {\n $ctx.state = 18;\n break;\n }\n case 11:\n _yieldValue = _aether._shouldYield;\n $ctx.state = 12;\n break;\n case 12:\n _aether._shouldYield = false;\n $ctx.state = 14;\n break;\n case 14:\n if (this.onAetherYield) {\n this.onAetherYield(_yieldValue);\n }\n $ctx.state = 16;\n break;\n case 16:\n $ctx.state = 8;\n return _yieldValue;\n case 8:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 10;\n break;\n case 10:\n if (typeof __yieldCount0 !== 'undefined' && __yieldCount0 !== null) {\n __yieldCount0++;\n }\n $ctx.state = 18;\n break;\n case 18:\n _aether.logStatementStart([{\n ofs: 152,\n row: 5,\n col: 4\n }, {\n ofs: 183,\n row: 5,\n col: 35\n }]);\n $ctx.state = 122;\n break;\n case 122:\n tmp5 = 'enemy';\n $ctx.state = 124;\n break;\n case 124:\n _aether.logStatement([{\n ofs: 152,\n row: 5,\n col: 4\n }, {\n ofs: 183,\n row: 5,\n col: 35\n }], _aether._userInfo, false);\n $ctx.state = 126;\n break;\n case 126:\n _aether.logStatementStart([{\n ofs: 152,\n row: 5,\n col: 4\n }, {\n ofs: 183,\n row: 5,\n col: 35\n }]);\n $ctx.state = 128;\n break;\n case 128:\n __global[tmp5] = tmp6;\n $ctx.state = 130;\n break;\n case 130:\n _aether.logStatement([{\n ofs: 152,\n row: 5,\n col: 4\n }, {\n ofs: 183,\n row: 5,\n col: 35\n }], _aether._userInfo, false);\n $ctx.state = 132;\n break;\n case 132:\n tmp10 = 'enemy';\n $ctx.state = 134;\n break;\n case 134:\n tmp11 = tmp10 in __global;\n $ctx.state = 136;\n break;\n case 136:\n if (tmp11) {\n tmp9 = __global[tmp10];\n } else {\n _aether.logStatementStart([{\n ofs: 192,\n row: 6,\n col: 7\n }, {\n ofs: 197,\n row: 6,\n col: 12\n }]);\n tmp12 = 'ReferenceError';\n tmp13 = __global[tmp12];\n tmp14 = new tmp13('ReferenceError: ' + (tmp10 + ' is not defined'));\n throw tmp14;\n _aether.logStatement([{\n ofs: 192,\n row: 6,\n col: 7\n }, {\n ofs: 197,\n row: 6,\n col: 12\n }], _aether._userInfo, false);\n }\n $ctx.state = 138;\n break;\n case 138:\n if (tmp9) {\n $ctx.state = 92;\n break;\n } else {\n $ctx.state = 108;\n break;\n }\n case 92:\n tmp16 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n $ctx.state = 93;\n break;\n case 93:\n tmp17 = 'isReady';\n $ctx.state = 95;\n break;\n case 95:\n _aether.logStatementStart([{\n ofs: 224,\n row: 7,\n col: 24\n }, {\n ofs: 232,\n row: 7,\n col: 32\n }]);\n $ctx.state = 97;\n break;\n case 97:\n tmp18 = 'cleave';\n $ctx.state = 99;\n break;\n case 99:\n _aether.logStatement([{\n ofs: 224,\n row: 7,\n col: 24\n }, {\n ofs: 232,\n row: 7,\n col: 32\n }], _aether._userInfo, false);\n $ctx.state = 101;\n break;\n case 101:\n _aether.logStatementStart([{\n ofs: 211,\n row: 7,\n col: 11\n }, {\n ofs: 233,\n row: 7,\n col: 33\n }]);\n $ctx.state = 103;\n break;\n case 103:\n tmp15 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp16[tmp17](_aether.restoreAPIClone(_aether, tmp18))));\n $ctx.state = 105;\n break;\n case 105:\n _aether.logStatement([{\n ofs: 211,\n row: 7,\n col: 11\n }, {\n ofs: 233,\n row: 7,\n col: 33\n }], _aether._userInfo, false);\n $ctx.state = 107;\n break;\n case 107:\n if (_aether._shouldYield) {\n $ctx.state = 24;\n break;\n } else {\n $ctx.state = 31;\n break;\n }\n case 24:\n _yieldValue = _aether._shouldYield;\n $ctx.state = 25;\n break;\n case 25:\n _aether._shouldYield = false;\n $ctx.state = 27;\n break;\n case 27:\n if (this.onAetherYield) {\n this.onAetherYield(_yieldValue);\n }\n $ctx.state = 29;\n break;\n case 29:\n $ctx.state = 21;\n return _yieldValue;\n case 21:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 23;\n break;\n case 23:\n if (typeof __yieldCount0 !== 'undefined' && __yieldCount0 !== null) {\n __yieldCount0++;\n }\n $ctx.state = 31;\n break;\n case 31:\n if (tmp15) {\n $ctx.state = 46;\n break;\n } else {\n $ctx.state = 75;\n break;\n }\n case 46:\n tmp19 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n $ctx.state = 47;\n break;\n case 47:\n tmp20 = 'cleave';\n $ctx.state = 49;\n break;\n case 49:\n tmp22 = 'enemy';\n $ctx.state = 51;\n break;\n case 51:\n tmp23 = tmp22 in __global;\n $ctx.state = 53;\n break;\n case 53:\n if (tmp23) {\n tmp21 = __global[tmp22];\n } else {\n _aether.logStatementStart([{\n ofs: 260,\n row: 8,\n col: 24\n }, {\n ofs: 265,\n row: 8,\n col: 29\n }]);\n tmp24 = 'ReferenceError';\n tmp25 = __global[tmp24];\n tmp26 = new tmp25('ReferenceError: ' + (tmp22 + ' is not defined'));\n throw tmp26;\n _aether.logStatement([{\n ofs: 260,\n row: 8,\n col: 24\n }, {\n ofs: 265,\n row: 8,\n col: 29\n }], _aether._userInfo, false);\n }\n $ctx.state = 55;\n break;\n case 55:\n _aether.logStatementStart([{\n ofs: 248,\n row: 8,\n col: 12\n }, {\n ofs: 266,\n row: 8,\n col: 30\n }]);\n $ctx.state = 57;\n break;\n case 57:\n tmp27 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp19[tmp20](_aether.restoreAPIClone(_aether, tmp21))));\n $ctx.state = 59;\n break;\n case 59:\n _aether.logStatement([{\n ofs: 248,\n row: 8,\n col: 12\n }, {\n ofs: 266,\n row: 8,\n col: 30\n }], _aether._userInfo, false);\n $ctx.state = 61;\n break;\n case 61:\n if (_aether._shouldYield) {\n $ctx.state = 37;\n break;\n } else {\n $ctx.state = 6;\n break;\n }\n case 37:\n _yieldValue = _aether._shouldYield;\n $ctx.state = 38;\n break;\n case 38:\n _aether._shouldYield = false;\n $ctx.state = 40;\n break;\n case 40:\n if (this.onAetherYield) {\n this.onAetherYield(_yieldValue);\n }\n $ctx.state = 42;\n break;\n case 42:\n $ctx.state = 34;\n return _yieldValue;\n case 34:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 36;\n break;\n case 36:\n if (typeof __yieldCount0 !== 'undefined' && __yieldCount0 !== null) {\n __yieldCount0++;\n }\n $ctx.state = 6;\n break;\n case 75:\n tmp28 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n $ctx.state = 76;\n break;\n case 76:\n tmp29 = 'attack';\n $ctx.state = 78;\n break;\n case 78:\n tmp31 = 'enemy';\n $ctx.state = 80;\n break;\n case 80:\n tmp32 = tmp31 in __global;\n $ctx.state = 82;\n break;\n case 82:\n if (tmp32) {\n tmp30 = __global[tmp31];\n } else {\n _aether.logStatementStart([{\n ofs: 319,\n row: 11,\n col: 24\n }, {\n ofs: 324,\n row: 11,\n col: 29\n }]);\n tmp33 = 'ReferenceError';\n tmp34 = __global[tmp33];\n tmp35 = new tmp34('ReferenceError: ' + (tmp31 + ' is not defined'));\n throw tmp35;\n _aether.logStatement([{\n ofs: 319,\n row: 11,\n col: 24\n }, {\n ofs: 324,\n row: 11,\n col: 29\n }], _aether._userInfo, false);\n }\n $ctx.state = 84;\n break;\n case 84:\n _aether.logStatementStart([{\n ofs: 307,\n row: 11,\n col: 12\n }, {\n ofs: 325,\n row: 11,\n col: 30\n }]);\n $ctx.state = 86;\n break;\n case 86:\n tmp36 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp28[tmp29](_aether.restoreAPIClone(_aether, tmp30))));\n $ctx.state = 88;\n break;\n case 88:\n _aether.logStatement([{\n ofs: 307,\n row: 11,\n col: 12\n }, {\n ofs: 325,\n row: 11,\n col: 30\n }], _aether._userInfo, false);\n $ctx.state = 90;\n break;\n case 90:\n if (_aether._shouldYield) {\n $ctx.state = 66;\n break;\n } else {\n $ctx.state = 6;\n break;\n }\n case 66:\n _yieldValue = _aether._shouldYield;\n $ctx.state = 67;\n break;\n case 67:\n _aether._shouldYield = false;\n $ctx.state = 69;\n break;\n case 69:\n if (this.onAetherYield) {\n this.onAetherYield(_yieldValue);\n }\n $ctx.state = 71;\n break;\n case 71:\n $ctx.state = 63;\n return _yieldValue;\n case 63:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 65;\n break;\n case 65:\n if (typeof __yieldCount0 !== 'undefined' && __yieldCount0 !== null) {\n __yieldCount0++;\n }\n $ctx.state = 6;\n break;\n case 108:\n {\n ;\n }\n $ctx.state = 6;\n break;\n case 6:\n _aether.logStatementStart([{\n ofs: 44,\n row: 2,\n col: 7\n }, {\n ofs: 48,\n row: 2,\n col: 11\n }]);\n $ctx.state = 147;\n break;\n case 147:\n tmp2 = true;\n $ctx.state = 149;\n break;\n case 149:\n _aether.logStatement([{\n ofs: 44,\n row: 2,\n col: 7\n }, {\n ofs: 48,\n row: 2,\n col: 11\n }], _aether._userInfo, false);\n $ctx.state = 151;\n break;\n case 151:\n if (this.onAetherYield) {\n this.onAetherYield('simple loop');\n }\n $ctx.state = 153;\n break;\n case 153:\n if (__yieldCount0 === 0) {\n $ctx.state = 139;\n break;\n } else {\n $ctx.state = 170;\n break;\n }\n case 139:\n $ctx.state = 140;\n return 'simple loop...';\n case 140:\n if ($ctx.action === 'throw') {\n $ctx.action = 'next';\n throw $ctx.sent;\n }\n $ctx.state = 170;\n break;\n case 3:\n _aether.logCallEnd();\n $ctx.state = 172;\n break;\n case 172:\n $ctx.state = -2;\n break;\n case -2:\n return $ctx;\n case -3:\n throw $ctx.storedException;\n default:\n throw 'traceur compiler bug: invalid state in state machine: ' + $ctx.state;\n }\n }, this);\n };\n tmp0 = 'plan';\n __global[tmp0] = tmp1;\n}(this));\n" } }, "submittedCodeLanguage": "javascript", "teamSpells": { - "common": ["coin-generator-9000/chooseAction"], - "humans": ["tharin/chooseAction"], - "ogres": ["mak-fod/chooseAction"] + "humans": ["hero-placeholder/plan"], + "ogres": ["hero-placeholder-1/plan"] }, - "levelID": "gold-rush", - "creatorName": "monkboll", - "creator": "53fdb782716abde208a0e7a4", - "totalScore": 24.174942315603005 - }] + "levelID": "dueling-grounds", + "creator": "5481b0fab5cf8a5f052ab62c", + "creatorName": "Lukucio Skriaudejas", + "totalScore": 29.794723041533086 + }], + "taskID": "54dd14c68bf17b9b05a92592", + "receiptHandle": "cbb4bc48f52de5f52dc38303ed960c0fc856ae61" } diff --git a/headless_client/worker_world.coffee b/headless_client/worker_world.coffee index 9406cec63..0b6c5d630 100644 --- a/headless_client/worker_world.coffee +++ b/headless_client/worker_world.coffee @@ -2,10 +2,11 @@ # This function needs to run inside an environment that has a 'self'. # This specific worker is targeted towards the node.js headless_client environment. -JASON = require 'jason' +JASON = require 'JASON' fs = require 'fs' GLOBAL.Aether = Aether = require 'aether' GLOBAL._ = _ = require 'lodash' +GLOBAL.CoffeeScript = require 'coffee-script' betterConsole = () -> @@ -80,6 +81,7 @@ work = () -> self.world.levelSessionIDs = args.levelSessionIDs self.world.submissionCount = args.submissionCount self.world.flagHistory = args.flagHistory + self.world.difficulty = args.difficulty self.world.loadFromLevel args.level, true if args.level self.world.headless = args.headless self.goalManager = new GoalManager(self.world) @@ -99,12 +101,12 @@ work = () -> self.postMessage type: 'start-load-frames' - self.world.loadFrames self.onWorldLoaded, self.onWorldError, self.onWorldLoadProgress, true + self.world.loadFrames self.onWorldLoaded, self.onWorldError, self.onWorldLoadProgress, null, true self.onWorldLoaded = onWorldLoaded = -> self.goalManager.worldGenerationEnded() goalStates = self.goalManager.getGoalStates() - self.postMessage type: 'end-load-frames', goalStates: goalStates + self.postMessage type: 'end-load-frames', goalStates: goalStates, overallStatus: goalManager.checkOverallStatus() t1 = new Date() diff = t1 - self.t0 @@ -147,7 +149,9 @@ work = () -> self.postedErrors[errorKey] = error else console.log 'Non-UserCodeError:', error.toString() + "\n" + error.stack or error.stackTrace + self.postMessage type: 'non-user-code-problem', problem: {message: error.toString()} self.cleanUp() + return false return true self.onWorldLoadProgress = onWorldLoadProgress = (progress) -> @@ -175,9 +179,19 @@ work = () -> self.postMessage type: 'worker-initialized' -worldCode = fs.readFileSync './public/javascripts/world.js', 'utf8' -lodashCode = fs.readFileSync './public/javascripts/lodash.js', 'utf8' -aetherCode = fs.readFileSync './public/javascripts/aether.js', 'utf8' +codeFileContents = [] +for codeFile in [ + 'lodash.js' + 'world.js' + 'aether.js' + 'app/vendor/aether-clojure.js' + 'app/vendor/aether-coffeescript.js' + 'app/vendor/aether-io.js' + 'app/vendor/aether-javascript.js' + 'app/vendor/aether-lua.js' + 'app/vendor/aether-python.js' + ] + codeFileContents.push fs.readFileSync("./public/javascripts/#{codeFile}", 'utf8') #window.BOX2D_ENABLED = true; @@ -194,9 +208,7 @@ ret = """ try { // the world javascript file - #{worldCode}; - #{lodashCode}; - #{aetherCode}; + #{codeFileContents.join(';\n ')}; // Don't let user generated code access stuff from our file system! self.importScripts = importScripts = null; diff --git a/karma.conf.js b/karma.conf.js index 5a4224edf..c7ed0175d 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,5 +1,3 @@ -// Testacular configuration -// Generated on Fri Feb 15 2013 18:38:33 GMT-0500 (EST) module.exports = function(config) { @@ -11,18 +9,19 @@ module.exports = function(config) { // list of files / patterns to load in the browser files : [ - 'public/javascripts/vendor.js', + 'public/javascripts/vendor.js', // need for jade definition... + 'public/javascripts/whole-vendor.js', 'public/lib/ace/ace.js', 'public/javascripts/aether.js', - 'public/javascripts/app.js', - 'public/javascripts/mock-ajax.js', - 'public/javascripts/test-app.js', + 'public/javascripts/whole-app.js', + 'public/javascripts/app/vendor/jasmine-mock-ajax.js', + 'public/javascripts/app/tests.js', 'public/javascripts/run-tests.js' ], preprocessors : { '**/*.coffee': 'coffee', - '**/javascripts/app.js': 'coverage' + '**/javascripts/whole-app.js': 'coverage' }, // list of files to exclude diff --git a/multicore.coffee b/multicore.coffee index d9a560513..de179e25e 100644 --- a/multicore.coffee +++ b/multicore.coffee @@ -1,14 +1,88 @@ cluster = require 'cluster' numCPUs = require('os').cpus().length +deaths = [ + 'Killed by a soldier ant.' + 'Ascended.' + 'Killed by an invisible gnome lord.' + 'Died of starvation.' + 'Petrified by a cockatrice corpse.' + 'Poisoned by a rotted kobold corpse.' + 'Fell into a pit.' + 'Killed by brainlessness.' + 'Slipped while mounting a saddled pony called Rainbow Dash.' + 'Killed by a scroll of genocide.' + 'Choked on a tin of spinach.' + 'Killed by the wrath of Anhur.' + 'Killed by a jackal, while fainted from lack of food.' + 'Drowned in a pool of water by an electric eel.' + 'Killed by falling downstairs.' + 'Killed by an ape, while helpless.' + 'Burned by a tower of flame.' + 'Killed by Ms. Sipaliwini, the shopkeeper.' + 'Fell onto a sink.' + 'Killed by a killer bee, while praying.' + 'Crushed to death by a collapsing drawbridge.' + 'Crunched in the head by an iron ball.' + 'Killed by exhaustion.' + 'Caught itself in its own magical blast.' + 'Shot itself with a death ray.' + 'Killed by genocidal confusion.' + 'Killed by touching Excalibur.' + 'Killed by a hallucinogen-distorted dwarf.' + 'Dissolved in molten lava.' + 'Turned to slime by a green slime.' + 'Killed by sipping boiling water.' + 'A trickery.' + 'Escaped (in celestial disgrace)' + 'Killed by kicking a sink.' + 'Killed by a kitten called Steve, while sleeping.' + 'Went to heaven prematurely.' + 'Teleported out of the dungeon and fell to its death.' + 'Panic.' + 'Killed by a minotaur, while dressing up.' + 'Petrified by trying to help a cockatrice out of a pit.' + 'Killed by a luckstone.' + 'Killed by a panther, while taking off clothes.' + 'Killed by a watch captain called The Nymphmaster.' + 'Killed by a black pudding, while jumping around.' + 'Killed by a hallucinogen-distorted white unicorn, while praying.' + 'Choked on a slice of birthday cake.' + 'Killed by a long worm, while reading a book.' + 'Killed by a giant beetle, while vomiting.' + 'Killed by an invisible master mind flayer, while unconscious from rotten food.' + 'Burned by burning.' + 'Killed by an air elemental, while hiding from thunderstorm (with the Amulet).' + 'Killed by wedging into a narrow crevice.' + 'Killed by a carnivorous bag.' + 'Killed by axing a hard object.' + 'Killed by an iron ball collision.' + 'Killed by an alchemic blast.' + 'Killed by dangerous winds.' + 'Killed by psychic blast.' + 'Committed suicide.' + 'Squished under a boulder.' + 'Killed by colliding with the ceiling.' + 'Killed by sitting on an iron spike.' + "Quit while already on Charon's boat." + 'Fell into a chasm.' + 'Turned to slime by a cockatrice egg.' +] + if cluster.isMaster - for i in [0...numCPUs] - cluster.fork() - cluster.on 'exit', (worker, code, signal) -> - console.log 'worker ' + worker.id + ' died' - cluster.fork() + for i in [0...numCPUs] + cluster.fork() + cluster.on 'exit', (worker, code, signal) -> + message = "Worker #{worker.id} died! #{deaths[Math.floor Math.random() * deaths.length]}" + console.log message + try + hipchat = require './server/hipchat' + hipchat.sendHipChatMessage(message, ['tower'], {papertrail: true}) + catch error + console.log "Couldn't send HipChat message on server death:", error + cluster.fork() else - require('coffee-script') - require('coffee-script/register') - server = require('./server') - server.startServer() + require('coffee-script') + require('coffee-script/register') + server = require('./server') + server.startServer() diff --git a/package.json b/package.json index 0929c6c6d..b4b2498fd 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,10 @@ { "name": "George Saines", "email": "george@codecombat.com" + }, + { + "name": "Matt Lott", + "email": "matt@codecombat.com" } ], "scripts": { @@ -38,70 +42,70 @@ "multiplayer" ], "dependencies": { - "express": "~3.0.6", - "winston": "0.6.x", - "passport": "0.1.x", - "passport-local": "0.1.x", - "moment": "~2.5.0", - "mongodb": "1.2.x", - "mongoose": "3.8.x", - "mongoose-text-search": "~0.0.2", - "request": "2.12.x", - "tv4": "~1.0.16", - "lodash": "~2.4.1", - "underscore.string": "2.3.x", - "async": "0.2.x", - "connect": "2.7.x", - "nodemailer": "0.4.x", - "coffee-script": "1.7.x", - "graceful-fs": "~2.0.1", - "node-force-domain": "~0.1.0", - "mailchimp-api": "2.0.x", - "express-useragent": "~0.0.9", - "gridfs-stream": "0.4.x", - "stream-buffers": "0.2.x", - "sendwithus": "2.1.x", - "aws-sdk": "~2.0.0", - "bayesian-battle": "0.0.x", - "redis": "", - "webworker-threads": "~0.4.11", - "node-gyp": "~0.13.0", - "aether": "~0.2.39", "JASON": "~0.1.3", "JQDeferred": "~2.1.0", + "aether": "~0.3.0", + "async": "0.2.x", + "aws-sdk": "~2.0.0", + "bayesian-battle": "0.0.x", + "coffee-script": "1.9.x", + "connect": "2.7.x", + "express": "~3.0.6", + "express-useragent": "~0.0.9", + "geoip-lite": "^1.1.6", + "graceful-fs": "~2.0.1", + "gridfs-stream": "0.4.x", "jsondiffpatch": "0.1.17", - "stripe": "~2.9.0" + "lodash": "~2.4.1", + "mailchimp-api": "2.0.x", + "moment": "~2.5.0", + "mongodb": "^2.0.28", + "mongoose": "3.8.x", + "mongoose-cache": "https://github.com/nwinter/mongoose-cache/tarball/master", + "node-force-domain": "~0.1.0", + "node-gyp": "~0.13.0", + "passport": "0.1.x", + "passport-local": "0.1.x", + "redis": "", + "request": "2.12.x", + "sendwithus": "2.1.x", + "stream-buffers": "0.2.x", + "stripe": "~2.9.0", + "tv4": "~1.0.16", + "underscore.string": "2.3.x", + "webworker-threads": "~0.5.5", + "winston": "0.6.x" }, "devDependencies": { - "jade": "0.33.x", - "javascript-brunch": "> 1.0 < 1.8", + "auto-reload-brunch": "> 1.0 < 1.8", + "bless-brunch": "https://github.com/ThomasConner/bless-brunch/tarball/master", + "bower": "~1.3.8", + "brunch": "~1.7.4", "coffee-script-brunch": "https://github.com/brunch/coffee-script-brunch/tarball/master", "coffeelint-brunch": "> 1.0 < 1.8", - "sass-brunch": "1.7.2", + "compressible": "~1.0.1", "css-brunch": "> 1.0 < 1.8", + "jade": "0.33.x", "jade-brunch": "> 1.0 < 1.8", - "uglify-js-brunch": "~1.7.4", - "auto-reload-brunch": "> 1.0 < 1.8", - "brunch": "~1.7.4", "jasmine-node": "1.13.x", - "nodemon": "0.7.5", - "marked": "0.2.x", - "telepath-brunch": "https://github.com/nwinter/telepath-brunch/tarball/master", - "bower": "~1.3.8", - "bless-brunch": "https://github.com/ThomasConner/bless-brunch/tarball/master", - "karma-script-launcher": "~0.1.0", + "jasmine-spec-reporter": "~0.3.0", + "javascript-brunch": "> 1.0 < 1.8", + "karma": "~0.12", "karma-chrome-launcher": "~0.1.2", + "karma-coffee-preprocessor": "~0.1.2", + "karma-coverage": "~0.1.4", "karma-firefox-launcher": "~0.1.3", "karma-html2js-preprocessor": "~0.1.0", - "karma-coffee-preprocessor": "~0.1.2", "karma-jasmine": "~0.2.0", - "requirejs": "~2.1.10", - "karma-requirejs": "~0.2.1", "karma-phantomjs-launcher": "~0.1.1", - "karma": "~0.12", - "karma-coverage": "~0.1.4", - "compressible": "~1.0.1", - "jasmine-spec-reporter": "~0.3.0" + "karma-requirejs": "~0.2.1", + "karma-script-launcher": "~0.1.0", + "marked": "0.2.x", + "nodemon": "0.7.5", + "requirejs": "~2.1.10", + "sass-brunch": "^1.8.8", + "telepath-brunch": "https://github.com/nwinter/telepath-brunch/tarball/master", + "uglify-js-brunch": "~1.7.4" }, "license": "MIT for the code, and CC-BY for the art and music", "private": true, diff --git a/scripts/analytics/insertStripeAnalytics.js b/scripts/analytics/insertStripeAnalytics.js new file mode 100644 index 000000000..af2092832 --- /dev/null +++ b/scripts/analytics/insertStripeAnalytics.js @@ -0,0 +1,86 @@ +// Insert older (-16 days) Stripe invoices into coco database analytics collection + +if (process.argv.length !== 4) { + log("Usage: node <script> <Stripe API key> <mongo connection Url>"); + process.exit(); +} + +var scriptStartTime = new Date(); +var stripeAPIKey = process.argv[2]; +var mongoConnUrl = process.argv[3]; +var stripe = require("stripe")(stripeAPIKey); +var MongoClient = require('mongodb').MongoClient; + +getInvoices(function(invoices) { + log("Invoice count: " + invoices.length); + insertInvoices(invoices, function() { + log("Script runtime: " + (new Date() - scriptStartTime)); + process.exit(0); + }); +}); + +function log(str) { + console.log(new Date().toISOString() + " " + str); +} + +function getInvoices(done) { + var sixteenDaysAgo = new Date() + sixteenDaysAgo.setUTCDate(sixteenDaysAgo.getUTCDate() - 16); + invoiceMaxTimestamp = Math.floor(sixteenDaysAgo.getTime() / 1000); + var options = {limit: 100, date: {lt: invoiceMaxTimestamp}}; + var invoices = []; + + getInvoicesHelper = function(options, done) { + // log("getInvoicesHelper " + invoices.length + " " + options.starting_after); + stripe.invoices.list(options, function (err, result) { + if (err) { + console.log(err); + return; + } + invoices = invoices.concat(result.data); + if (result.has_more) { + options.starting_after = result.data[result.data.length - 1].id + getInvoicesHelper(options, done); + } + else { + done(invoices); + } + }); + }; + getInvoicesHelper(options, done); +} + +function insertInvoices(invoices, done) { + var docs = []; + for (var i = 0; i < invoices.length; i++) { + docs.push({ + _id: invoices[i].id, + date: invoices[i].date, + properties: invoices[i] + }); + } + + MongoClient.connect(mongoConnUrl, function (err, db) { + if (err) { + console.log(err); + return done(); + } + + insertInvoicesHelper = function() { + var doc = docs.pop(); + if (!doc) { + db.close(); + return done(); + } + db.collection('analytics.stripe.invoices').save(doc, function(err, result) { + if (err) { + console.log(err); + db.close(); + return done(); + } + insertInvoicesHelper(); + }); + }; + insertInvoicesHelper(); + }); +} diff --git a/scripts/analytics/mixpanel.py b/scripts/analytics/mixpanel.py new file mode 100644 index 000000000..9a6067a93 --- /dev/null +++ b/scripts/analytics/mixpanel.py @@ -0,0 +1,135 @@ +#! /usr/bin/env python +# +# Mixpanel, Inc. -- http://mixpanel.com/ +# +# Python API client library to consume mixpanel.com analytics data. +# +# Copyright 2010-2013 Mixpanel, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import hashlib +import urllib +import urllib2 +import time +try: + import json +except ImportError: + import simplejson as json + +class Mixpanel(object): + + # https right? + ENDPOINT = 'http://mixpanel.com/api' + DATA_ENDPOINT = 'http://data.mixpanel.com/api' + VERSION = '2.0' + + def __init__(self, api_key, api_secret): + self.api_key = api_key + self.api_secret = api_secret + + def request(self, methods, params, format='json'): + """ + methods - List of methods to be joined, e.g. ['events', 'properties', 'values'] + will give us http://mixpanel.com/api/2.0/events/properties/values/ + params - Extra parameters associated with method + """ + params['api_key'] = self.api_key + params['expire'] = int(time.time()) + 600 # Grant this request 10 minutes. + params['format'] = format + + if 'sig' in params: del params['sig'] + params['sig'] = self.hash_args(params) + + if 'export' in methods: + request_url = '/'.join([self.DATA_ENDPOINT, str(self.VERSION)] + methods) + '/?' + self.unicode_urlencode(params) + else: + request_url = '/'.join([self.ENDPOINT, str(self.VERSION)] + methods) + '/?' + self.unicode_urlencode(params) + request = urllib2.urlopen(request_url, timeout=120) + data = request.read() + # return json.loads(data) + return data + + def unicode_urlencode(self, params): + """ + Convert lists to JSON encoded strings, and correctly handle any + unicode URL parameters. + """ + if isinstance(params, dict): + params = params.items() + for i, param in enumerate(params): + if isinstance(param[1], list): + params[i] = (param[0], json.dumps(param[1]),) + + return urllib.urlencode( + [(k, isinstance(v, unicode) and v.encode('utf-8') or v) for k, v in params] + ) + + def hash_args(self, args, secret=None): + """ + Hashes arguments by joining key=value pairs, appending a secret, and + then taking the MD5 hex digest. + """ + for a in args: + if isinstance(args[a], list): args[a] = json.dumps(args[a]) + + args_joined = '' + for a in sorted(args.keys()): + if isinstance(a, unicode): + args_joined += a.encode('utf-8') + else: + args_joined += str(a) + + args_joined += '=' + + if isinstance(args[a], unicode): + args_joined += args[a].encode('utf-8') + else: + args_joined += str(args[a]) + + hash = hashlib.md5(args_joined) + + if secret: + hash.update(secret) + elif self.api_secret: + hash.update(self.api_secret) + return hash.hexdigest() + +if __name__ == '__main__': + api = Mixpanel( + api_key = 'YOUR_API_KEY', + api_secret = 'YOUR_API_SECRET' + ) + # data = api.request(['events'], { + # 'event' : ['Finished subscription purchase',], + # 'unit' : 'hour', + # 'interval' : 24, + # 'type': 'general' + # }) + + # data = api.request(['funnels', 'list'], {}) + + data = api.request(['export'], { + 'event' : ['Finished subscription purchase',], + 'from_date' : '2014-14-05', + 'to_date' : '2014-14-06' + }) + for line in data.split('\n'): + try: + if len(line): + event = json.loads(line) + print event['event'], event['properties']['distinct_id'] + except: + print 'error' + print line + diff --git a/scripts/analytics/mixpanelABGemPrompt.py b/scripts/analytics/mixpanelABGemPrompt.py new file mode 100644 index 000000000..43682f4ea --- /dev/null +++ b/scripts/analytics/mixpanelABGemPrompt.py @@ -0,0 +1,122 @@ +# Calculate gem prompt A/B test results + +# TODO: Why is no-prompt group 50% larger? + +import sys +from mixpanel import Mixpanel + +try: + import json +except ImportError: + import simplejson as json + +# NOTE: mixpanel dates are by day and inclusive +# E.g. '2014-12-08' is any date that day, up to 2014-12-09 12am + +if __name__ == '__main__': + if not len(sys.argv) is 3: + print "Script format: <script> <api_key> <api_secret>" + else: + api_key = sys.argv[1] + api_secret = sys.argv[2] + api = Mixpanel( + api_key = api_key, + api_secret = api_secret + ) + + startDate = '2015-01-15' + startDate = '2014-11-25' + endDate = '2015-02-11' + + print("Requesting data for {0} to {1}".format(startDate, endDate)) + data = api.request(['export'], { + 'event' : ['Started purchase', 'Finished gem purchase'], + 'from_date' : startDate, + 'to_date' : endDate + }) + + userProgressionGroupA = {} + userProgressionGroupB = {} + + lines = data.split('\n') + print "Received %d entries" % len(lines) + for line in lines: + try: + if len(line) is 0: continue + eventData = json.loads(line) + eventName = eventData['event'] + properties = eventData['properties'] + if not eventName in ['Started purchase', 'Finished gem purchase']: + print 'Unexpected event ' + eventName + break + if 'distinct_id' in properties and 'gemPromptGroup' in properties: + userID = properties['distinct_id'] + if properties['gemPromptGroup'] == 'prompt': + if not userID in userProgressionGroupA: + userProgressionGroupA[userID] = { + 'Started purchase': 0, + 'Finished gem purchase': 0 + } + userProgressionGroupA[userID][eventName] += 1 + elif properties['gemPromptGroup'] == 'no-prompt': + if not userID in userProgressionGroupB: + userProgressionGroupB[userID] = { + 'Started purchase': 0, + 'Finished gem purchase': 0 + } + userProgressionGroupB[userID][eventName] += 1 + else: + print "Unexpected group:", properties['gemPromptGroup'] + print properties + print line + break + except: + print "Unexpected error:", sys.exc_info()[0] + print line + break + + try: + started = converted = 0 + startedGroupA = convertedGroupA = 0 + startedGroupB = convertedGroupB = 0 + + # Group A + print("Processing Group A") + for key, item in userProgressionGroupA.iteritems(): + if item['Finished gem purchase'] > 0: + converted += 1 + convertedGroupA += 1 + # TODO: is our distinct_id correct? We hit this at least once. + # if item['Finished gem purchase'] > 1: + # print "User multiple subcription purchases?" + # print item + elif item['Started purchase'] > 0: + started += 1 + startedGroupA += 1 + else: + print "User without any hits?" + print item + break + + # Group B + print("Processing Group B") + for key, item in userProgressionGroupB.iteritems(): + if item['Finished gem purchase'] > 0: + converted += 1 + convertedGroupB += 1 + elif item['Started purchase'] > 0: + started += 1 + startedGroupB += 1 + else: + print "User without any hits?" + print item + break + + print("Overall") + print("started {0} converted {1} rate {2}%".format(started, converted, float(converted) / started * 100)) + print("Group prompt") + print("startedGroupA {0} convertedGroupA {1} rate {2}%".format(startedGroupA, convertedGroupA, float(convertedGroupA) / startedGroupA * 100)) + print("Group no-prompt") + print("startedGroupB {0} convertedGroupB {1} rate {2}%".format(startedGroupB, convertedGroupB, float(convertedGroupB) / startedGroupB * 100)) + except: + print "Unexpected error:", sys.exc_info()[0] diff --git a/scripts/analytics/mixpanelABSubscribeCopy.py b/scripts/analytics/mixpanelABSubscribeCopy.py new file mode 100644 index 000000000..16f406ed9 --- /dev/null +++ b/scripts/analytics/mixpanelABSubscribeCopy.py @@ -0,0 +1,130 @@ +# Calculate subscribe copy A/B test results + +import sys +from mixpanel import Mixpanel + +try: + import json +except ImportError: + import simplejson as json + +# NOTE: mixpanel dates are by day and inclusive +# E.g. '2014-12-08' is any date that day, up to 2014-12-09 12am + +if __name__ == '__main__': + if not len(sys.argv) is 3: + print "Script format: <script> <api_key> <api_secret>" + else: + api_key = sys.argv[1] + api_secret = sys.argv[2] + api = Mixpanel( + api_key = api_key, + api_secret = api_secret + ) + + startDate = '2014-12-14' + endDate = '2014-12-21' + print("Requesting data for {0} to {1}".format(startDate, endDate)) + data = api.request(['export'], { + 'event' : ['Show subscription modal', 'Started subscription purchase', 'Finished subscription purchase'], + 'from_date' : startDate, + 'to_date' : endDate + }) + + userProgressionGroupA = {} + userProgressionGroupB = {} + + lines = data.split('\n') + print "Received %d entries" % len(lines) + for line in lines: + try: + if len(line) is 0: continue + eventData = json.loads(line) + eventName = eventData['event'] + properties = eventData['properties'] + if not eventName in ['Show subscription modal', 'Started subscription purchase', 'Finished subscription purchase']: + print 'Unexpected event ' + eventName + break + if 'distinct_id' in properties and 'testGroupNumber' in properties: + userID = properties['distinct_id'] + # Test grouping logic + # group = me.get('testGroupNumber') % 6 + # @subscribeCopyGroup = switch group + # when 0, 1, 2 then 'original' + # when 3, 4, 5 then 'new' + if int(properties['testGroupNumber']) % 6 in [0, 1, 2]: + if not userID in userProgressionGroupA: + userProgressionGroupA[userID] = { + 'Show subscription modal': 0, + 'Started subscription purchase': 0, + 'Finished subscription purchase': 0 + } + userProgressionGroupA[userID][eventName] += 1 + else: + if not userID in userProgressionGroupB: + userProgressionGroupB[userID] = { + 'Show subscription modal': 0, + 'Started subscription purchase': 0, + 'Finished subscription purchase': 0 + } + userProgressionGroupB[userID][eventName] += 1 + except: + print "Unexpected error:", sys.exc_info()[0] + print line + break + + + try: + saw = started = converted = 0 + sawGroupA = startedGroupA = convertedGroupA = 0 + sawGroupB = startedGroupB = convertedGroupB = 0 + + # Group A + print("Processing Group A") + for key, item in userProgressionGroupA.iteritems(): + if item['Finished subscription purchase'] > 0: + converted += 1 + convertedGroupA += 1 + # TODO: is our distinct_id correct? We hit this at least once. + # if item['Finished subscription purchase'] > 1: + # print "User multiple subcription purchases?" + # print item + elif item['Started subscription purchase'] > 0: + started += 1 + startedGroupA += 1 + elif item['Show subscription modal'] > 0: + saw += 1 + sawGroupA += 1 + else: + print "User without any hits?" + print item + break + + # Group B + print("Processing Group B") + for key, item in userProgressionGroupB.iteritems(): + if item['Finished subscription purchase'] > 0: + converted += 1 + convertedGroupB += 1 + elif item['Started subscription purchase'] > 0: + started += 1 + startedGroupB += 1 + elif item['Show subscription modal'] > 0: + saw += 1 + sawGroupB += 1 + else: + print "User without any hits?" + print item + break + + print("Overall") + print("saw {0} started {1} converted {2}".format(saw, started, converted)) + print("step 1 conversion {0}% step 2 conversion {1}% overall conversion {2}%".format(float(started) / saw * 100, float(converted) / started * 100, float(converted) / saw * 100)) + print("Group A") + print("sawGroupA {0} startedGroupA {1} convertedGroupA {2}".format(sawGroupA, startedGroupA, convertedGroupA)) + print("step 1 conversion {0}% step 2 conversion {1}% overall conversion {2}%".format(float(startedGroupA) / sawGroupA * 100, float(convertedGroupA) / startedGroupA * 100, float(convertedGroupA) / sawGroupA * 100)) + print("Group B") + print("sawGroupB {0} startedGroupB {1} convertedGroupB {2}".format(sawGroupB, startedGroupB, convertedGroupB)) + print("step 1 conversion {0}% step 2 conversion {1}% overall conversion {2}%".format(float(startedGroupB) / sawGroupB * 100, float(convertedGroupB) / startedGroupB * 100, float(convertedGroupB) / sawGroupB * 100)) + except: + print "Unexpected error:", sys.exc_info()[0] diff --git a/scripts/analytics/mixpanelGetEvent.py b/scripts/analytics/mixpanelGetEvent.py new file mode 100644 index 000000000..5a4fd53c5 --- /dev/null +++ b/scripts/analytics/mixpanelGetEvent.py @@ -0,0 +1,184 @@ +# Get mixpanel event data via export API +# Useful for debugging Mixpanel data weirdness + +targetLevels = ['dungeons-of-kithgard', 'the-raised-sword', 'endangered-burl'] +targetLevels = ['dungeons-of-kithgard'] +eventFunnel = ['Started Level', 'Saw Victory'] +# eventFunnel = ['Saw Victory'] +# eventFunnel = ['Started Level'] + +import sys +from pprint import pprint +from datetime import datetime, timedelta +from mixpanel import Mixpanel + +try: + import json +except ImportError: + import simplejson as json + +# NOTE: mixpanel dates are by day and inclusive +# E.g. '2014-12-08' is any date that day, up to 2014-12-09 12am + +if __name__ == '__main__': + if not len(sys.argv) is 3: + print "Script format: <script> <api_key> <api_secret>" + else: + scriptStart = datetime.now() + + api_key = sys.argv[1] + api_secret = sys.argv[2] + api = Mixpanel( + api_key = api_key, + api_secret = api_secret + ) + + startDate = '2015-01-01' + endDate = '2015-01-26' + + startEvent = eventFunnel[0] + endEvent = eventFunnel[-1] + + print("Requesting data for {0} to {1}".format(startDate, endDate)) + data = api.request(['export'], { + # 'where': '"539c630f30a67c3b05d98d95" == properties["id"]', + # 'where': "('539c630f30a67c3b05d98d95' == properties['id'] or '539c630f30a67c3b05d98d95' == properties['distinct_id'])", + 'event': eventFunnel, + 'from_date': startDate, + 'to_date': endDate + }) + + + weirdUserIDs = [] + eventUsers = {} + levelEventUserDayMap = {} + levelUserEventDayMap = {} + lines = data.split('\n') + print "Received %d entries" % len(lines) + for line in lines: + try: + if len(line) is 0: continue + eventData = json.loads(line) + # pprint(eventData) + # break + eventName = eventData['event'] + if not eventName in eventFunnel: + print 'Unexpected event ' + eventName + break + if not 'properties' in eventData: + print('no properties, skpping') + continue + properties = eventData['properties'] + if not 'distinct_id' in properties: + print('no distinct_id, skpping') + continue + user = properties['distinct_id'] + if not 'time' in properties: + print('no time, skpping') + continue + time = properties['time'] + pst = datetime.fromtimestamp(int(properties['time'])) + utc = pst + timedelta(0, 8 * 60 * 60) + dateCreated = utc.isoformat() + day = dateCreated[0:10] + if day < startDate or day > endDate: + print "Skipping {0}".format(day) + continue + + if 'levelID' in properties: + level = properties['levelID'] + elif 'level' in properties: + level = properties['level'].lower().replace(' ', '-') + else: + print("Unkonwn level for", eventName) + print(properties) + break + + if not level in targetLevels: continue + + # if user != "539c630f30a67c3b05d98d95": continue + pprint(eventData) + + # if user == "54c1fc3a08652d5305442c6b": + # pprint(eventData) + # break + # if '-' in user: + # weirdUserIDs.append(user) + # # pprint(eventData) + # # break + # continue + + # print level + + if not level in levelEventUserDayMap: levelEventUserDayMap[level] = {} + if not eventName in levelEventUserDayMap[level]: levelEventUserDayMap[level][eventName] = {} + if not user in levelEventUserDayMap[level][eventName] or levelEventUserDayMap[level][eventName][user] > day: + levelEventUserDayMap[level][eventName][user] = day + + if not user in eventUsers: eventUsers[user] = True + + if not level in levelUserEventDayMap: levelUserEventDayMap[level] = {} + if not user in levelUserEventDayMap[level]: levelUserEventDayMap[level][user] = {} + if not eventName in levelUserEventDayMap[level][user] or levelUserEventDayMap[level][user][eventName] > day: + levelUserEventDayMap[level][user][eventName] = day + + except: + print "Unexpected error:", sys.exc_info()[0] + print line + break + + # pprint(levelEventUserDayMap) + + print("Weird user IDs: {0}".format(len(weirdUserIDs))) + + for level in levelEventUserDayMap: + for event in levelEventUserDayMap[level]: + print("{0} {1} {2}".format(level, event, len(levelEventUserDayMap[level][event]))) + print("Users: {0}".format(len(eventUsers))) + + noStartDayUsers = [] + levelFunnelData = {} + for level in levelUserEventDayMap: + for user in levelUserEventDayMap[level]: + # 6455 + # for event in levelUserEventDayMap[level][user]: + # day = levelUserEventDayMap[level][user][event] + # if not level in levelFunnelData: levelFunnelData[level] = {} + # if not day in levelFunnelData[level]: levelFunnelData[level][day] = {} + # if not event in levelFunnelData[level][day]: levelFunnelData[level][day][event] = 0 + # levelFunnelData[level][day][event] += 1 + + # 5382 + funnelStartDay = None + for event in levelUserEventDayMap[level][user]: + day = levelUserEventDayMap[level][user][event] + if not level in levelFunnelData: levelFunnelData[level] = {} + if not day in levelFunnelData[level]: levelFunnelData[level][day] = {} + if not event in levelFunnelData[level][day]: levelFunnelData[level][day][event] = 0 + if eventFunnel[0] == event: + levelFunnelData[level][day][event] += 1 + funnelStartDay = day + break + + if funnelStartDay: + for event in levelUserEventDayMap[level][user]: + if not event in levelFunnelData[level][funnelStartDay]: + levelFunnelData[level][funnelStartDay][event] = 0 + if eventFunnel[0] != event: + levelFunnelData[level][funnelStartDay][event] += 1 + for i in range(1, len(eventFunnel)): + event = eventFunnel[i] + if not event in levelFunnelData[level][funnelStartDay]: + levelFunnelData[level][funnelStartDay][event] = 0 + else: + noStartDayUsers.append(user) + + pprint(levelFunnelData) + print("No start day count: {0}".format(len(noStartDayUsers))) + noStartDayUsers.sort() + for i in range(len(noStartDayUsers)): + if i > 50: break + print(noStartDayUsers[i]) + + + print("Script runtime: {0}".format(datetime.now() - scriptStart)) diff --git a/scripts/analytics/mixpanelLevelRates.py b/scripts/analytics/mixpanelLevelRates.py new file mode 100644 index 000000000..de4948625 --- /dev/null +++ b/scripts/analytics/mixpanelLevelRates.py @@ -0,0 +1,158 @@ +# Calculate level completion rates via mixpanel export API + +# TODO: why are our 'time' fields in PST time? + +targetLevels = ['dungeons-of-kithgard', 'the-raised-sword', 'endangered-burl'] +eventFunnel = ['Started Level', 'Saw Victory'] + +import sys + +from datetime import datetime, timedelta +from mixpanel import Mixpanel + +try: + import json +except ImportError: + import simplejson as json + +# NOTE: mixpanel dates are by day and inclusive +# E.g. '2014-12-08' is any date that day, up to 2014-12-09 12am + +if __name__ == '__main__': + if not len(sys.argv) is 3: + print "Script format: <script> <api_key> <api_secret>" + else: + scriptStart = datetime.now() + + api_key = sys.argv[1] + api_secret = sys.argv[2] + api = Mixpanel( + api_key = api_key, + api_secret = api_secret + ) + + # startDate = '2015-01-11' + # endDate = '2015-01-17' + startDate = '2015-01-23' + endDate = '2015-01-23' + # endDate = '2015-01-28' + + startEvent = eventFunnel[0] + endEvent = eventFunnel[-1] + + print("Requesting data for {0} to {1}".format(startDate, endDate)) + data = api.request(['export'], { + 'event' : eventFunnel, + 'from_date' : startDate, + 'to_date' : endDate + }) + + + # Map ordering: level, user, event, day + userDataMap = {} + lines = data.split('\n') + print "Received %d entries" % len(lines) + for line in lines: + try: + if len(line) is 0: continue + eventData = json.loads(line) + eventName = eventData['event'] + if not eventName in eventFunnel: + print 'Unexpected event ' + eventName + break + if not 'properties' in eventData: continue + properties = eventData['properties'] + if not 'distinct_id' in properties: continue + user = properties['distinct_id'] + if not 'time' in properties: continue + time = properties['time'] + pst = datetime.fromtimestamp(int(properties['time'])) + utc = pst + timedelta(0, 8 * 60 * 60) + dateCreated = utc.isoformat() + day = dateCreated[0:10] + if day < startDate or day > endDate: + print "Skipping {0}".format(day) + continue + + if 'levelID' in properties: + level = properties['levelID'] + elif 'level' in properties: + level = properties['level'].lower().replace(' ', '-') + else: + print("Unkonwn level for", eventName) + print(properties) + break + + if not level in targetLevels: + continue + + # print level + + if not level in userDataMap: userDataMap[level] = {} + if not user in userDataMap[level]: userDataMap[level][user] = {} + if not eventName in userDataMap[level][user] or userDataMap[level][user][eventName] > day: + userDataMap[level][user][eventName] = day + except: + print "Unexpected error:", sys.exc_info()[0] + print line + break + + # print(userDataMap) + + levelFunnelData = {} + for level in userDataMap: + for user in userDataMap[level]: + funnelStartDay = None + for event in userDataMap[level][user]: + day = userDataMap[level][user][event] + if not level in levelFunnelData: levelFunnelData[level] = {} + if not day in levelFunnelData[level]: levelFunnelData[level][day] = {} + if not event in levelFunnelData[level][day]: levelFunnelData[level][day][event] = 0 + if eventFunnel[0] == event: + levelFunnelData[level][day][event] += 1 + funnelStartDay = day + break + + if funnelStartDay: + for event in userDataMap[level][user]: + if not event in levelFunnelData[level][funnelStartDay]: + levelFunnelData[level][funnelStartDay][event] = 0 + if not eventFunnel[0] == event: + levelFunnelData[level][funnelStartDay][event] += 1 + for i in range(1, len(eventFunnel)): + event = eventFunnel[i] + if not event in levelFunnelData[level][funnelStartDay]: + levelFunnelData[level][funnelStartDay][event] = 0 + + # print(levelFunnelData) + + totals = {} + for level in levelFunnelData: + for day in levelFunnelData[level]: + if startEvent in levelFunnelData[level][day]: + started = levelFunnelData[level][day][startEvent] + else: + started = 0 + if endEvent in levelFunnelData[level][day]: + finished = levelFunnelData[level][day][endEvent] + else: + finished = 0 + if not level in totals: totals[level] = {} + if not startEvent in totals[level]: totals[level][startEvent] = 0 + if not endEvent in totals[level]: totals[level][endEvent] = 0 + totals[level][startEvent] += started + totals[level][endEvent] += finished + if started > 0: + print("{0}\t{1}\t{2}\t{3}\t{4}%".format(level, day, started, finished, float(finished) / started * 100)) + else: + print("{0}\t{1}\t{2}\t{3}\t".format(level, day, started, finished)) + + for level in totals: + started = totals[level][startEvent] + finished = totals[level][endEvent] + if started > 0: + print("{0}\t{1}\t{2}\t{3}%".format(level, started, finished, float(finished) / started * 100)) + else: + print("{0}\t{1}\t{2}\t".format(level, started, finished)) + + print("Script runtime: {0}".format(datetime.now() - scriptStart)) diff --git a/scripts/analytics/mongodb/queries/CodeLanguageUsage.js b/scripts/analytics/mongodb/queries/CodeLanguageUsage.js new file mode 100644 index 000000000..c44e59d4c --- /dev/null +++ b/scripts/analytics/mongodb/queries/CodeLanguageUsage.js @@ -0,0 +1,35 @@ +// Print out code language usage based on level session data + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +var total = 0; +var languages = {}; +var startDate = new ISODate("2014-12-01T00:00:00.000Z") +var cursor = db['level.sessions'].aggregate( +[ + { $match : { + //$and: [{codeLanguage: {$exists: true}}, {created : { $gte: startDate}}] + codeLanguage: {$exists: true} + } + }, + { + $group : { + _id: "$codeLanguage", + total: {$sum: 1} + } + }, + { $sort : { total : -1} } +]); + +while (cursor.hasNext()) { + var myDoc = cursor.next(); + total += myDoc.total; + if (!languages[myDoc._id]) + languages[myDoc._id] = 0 + languages[myDoc._id] += myDoc.total +} +print("Total sessions with code languages", total); +for (key in languages) { + print(key + "\t" + languages[key] + "\t" + (languages[key] / total * 100).toFixed(2) + "%"); +} \ No newline at end of file diff --git a/scripts/analytics/mongodb/queries/GemsPerDay.js b/scripts/analytics/mongodb/queries/GemsPerDay.js new file mode 100644 index 000000000..5adfa3a29 --- /dev/null +++ b/scripts/analytics/mongodb/queries/GemsPerDay.js @@ -0,0 +1,51 @@ +// Print out gem counts bucketed by day and amount + + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> +// Usage restricted to HoC Dates: +// mongo <address>:<port>/<database> -eval "var startDate='2014-12-08T00:00:00.000Z', endDate='2014-12-14T00:00:00.000Z';" <script file> -u <username> -p <password> + +var match = null; + +if (typeof startDate !== "undefined" && startDate !== null && typeof endDate !== "undefined" && endDate !== null) { + print("Using dates " + startDate + " to " + endDate); + match={ + "$match" : { + $and : [ + {"created": { $gte: startDate}}, + {"created": { $lt: endDate}}, + {"stripe.subscriptionID" : { "$exists" : false }} + ] + } + }; +} else { + print("No date range specified."); + match={ + "$match" : {"stripe.subscriptionID" : { "$exists" : false }} + }; +} + +var proj0 = {"$project": { + "amount": 1, + "created": { "$concat": [{"$substr" : ["$created", 0, 4]}, "-", {"$substr" : ["$created", 5, 2]}, "-", {"$substr" : ["$created", 8, 2]}]} +} +}; + +var group={"$group" : { + "_id" : { + "m" : "$amount", + "d" : "$created" + }, + "count" : { + "$sum" : 1 + } + } +}; + +var sort = {$sort: { "_id.d" : -1}}; +var cursor = db.payments.aggregate(match, proj0, group, sort); +while (cursor.hasNext()) { + var doc = cursor.next(); + print(doc._id.d + "\t" + doc._id.m + "\t" + doc.count); +} \ No newline at end of file diff --git a/scripts/analytics/mongodb/queries/LevelPlayedAfterSub.js b/scripts/analytics/mongodb/queries/LevelPlayedAfterSub.js new file mode 100644 index 000000000..418178384 --- /dev/null +++ b/scripts/analytics/mongodb/queries/LevelPlayedAfterSub.js @@ -0,0 +1,69 @@ +// Find completed level counts immediately after subscription purchase + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> +// Usage restricted to HoC Dates: +// mongo <address>:<port>/<database> -eval "var startDate='2014-12-08T00:00:00.000Z', endDate='2014-12-14T00:00:00.000Z';" <script file> -u <username> -p <password> + +// TODO: This is nearly identical to LevelPlayedBeforeSub.js + +var nextLevelPlayedCompleted = {}; +var paymentsCursor; + +if (typeof startDate !== "undefined" && startDate !== null && typeof endDate !== "undefined" && endDate !== null) { + print("Using dates " + startDate + " to " + endDate); + paymentsCursor = db.payments.find({ + $and: [ + {"created": { $gte: startDate}}, + {"created": { $lt: endDate}}, + {"stripe.subscriptionID" : { "$exists" : true }} + ] + }); +} else { + print("No date range specified."); + paymentsCursor = db.payments.find({"stripe.subscriptionID" : { "$exists" : true }}); +} + +while (paymentsCursor.hasNext()) { + var doc = paymentsCursor.next(); + var ID = doc._id; + var purchaseDate = doc.created; + + // Find next level session completed + var levelSessionCursor = db['level.sessions'].find({ + $and: [{"state.complete" : true}, {creator : doc.purchaser.valueOf()}, {created: {$gt: ISODate(purchaseDate)}}] + }).sort({created: 1}); + + if (levelSessionCursor.hasNext()) { + var nextLevelSession = levelSessionCursor.next(); + + // Find last level completed + var levelCursor = db.levels.find({"original" : ObjectId(nextLevelSession.level.original), "version.isLatestMajor": true, "version.isLatestMinor": true}) + if (levelCursor.hasNext()) { + var nextLevel = levelCursor.next(); + if (!nextLevelPlayedCompleted[nextLevel.name]) + nextLevelPlayedCompleted[nextLevel.name] = 0; + nextLevelPlayedCompleted[nextLevel.name]++; + } + else { + if (!nextLevelPlayedCompleted['unknown']) + nextLevelPlayedCompleted['unknown'] = 0; + nextLevelPlayedCompleted['unknown']++; + } + } + else { + if (!nextLevelPlayedCompleted['unknown']) + nextLevelPlayedCompleted['unknown'] = 0; + nextLevelPlayedCompleted['unknown']++; + } +} + +// Sort descending count and print +var sorted = []; +for (key in nextLevelPlayedCompleted) { + sorted.push({name: key, count: nextLevelPlayedCompleted[key]}); +} +sorted.sort(function(a,b) { return b.count - a.count}); +for (var i = 0; i < sorted.length; i++) { + print(sorted[i].count + "\t" + sorted[i].name); +} diff --git a/scripts/analytics/mongodb/queries/LevelPlayedBeforeSub.js b/scripts/analytics/mongodb/queries/LevelPlayedBeforeSub.js new file mode 100644 index 000000000..ad269b03f --- /dev/null +++ b/scripts/analytics/mongodb/queries/LevelPlayedBeforeSub.js @@ -0,0 +1,74 @@ +// Find completed level counts immediately before subscription purchase + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> +// Usage restricted to HoC Dates: +// mongo <address>:<port>/<database> -eval "var startDate='2014-12-08T00:00:00.000Z', endDate='2014-12-14T00:00:00.000Z';" <script file> -u <username> -p <password> + +// TODO: query against _id instead of created +// TODO: Must be a better way to query for this data all at once. + +var startTime = new Date(); + +var lastLevelCompleted = {}; +var paymentsCursor; + +if (typeof startDate !== "undefined" && startDate !== null && typeof endDate !== "undefined" && endDate !== null) { + print("Using dates " + startDate + " to " + endDate); + paymentsCursor = db.payments.find({ + $and: [ + {"created": { $gte: startDate}}, + {"created": { $lt: endDate}}, + {"stripe.subscriptionID" : { "$exists" : true }} + ] + }); +} else { + print("No date range specified"); + paymentsCursor = db.payments.find({"stripe.subscriptionID": { "$exists" : true }}); +} + +while (paymentsCursor.hasNext()) { + var doc = paymentsCursor.next(); + var purchaseDate = doc.created; + var user = doc.purchaser.valueOf(); + + // print("Processing purchase on " + purchaseDate + " for " + user); + + // Find last level session completed + var levelSessionCursor = db['level.sessions'].find({ + $and: [{"state.complete" : true}, {creator : user}, {changed: {$lt: ISODate(purchaseDate)}}] + }).sort({created: -1}); + if (levelSessionCursor.hasNext()) { + var lastLevelSessionCompleted = levelSessionCursor.next(); + + // Find last level completed + var levelCursor = db.levels.find({"original" : ObjectId(lastLevelSessionCompleted.level.original), "version.isLatestMajor": true, "version.isLatestMinor": true}) + if (levelCursor.hasNext()) { + var lastLevel = levelCursor.next(); + if (!lastLevelCompleted[lastLevel.name]) lastLevelCompleted[lastLevel.name] = 0; + lastLevelCompleted[lastLevel.name]++; + } + else { + if (!lastLevelCompleted['unknown']) lastLevelCompleted['unknown'] = 0; + lastLevelCompleted['unknown']++; + } + } + else { + if (!lastLevelCompleted['unknown']) lastLevelCompleted['unknown'] = 0; + lastLevelCompleted['unknown']++; + } +} + +// Sort descending count and print +var sorted = []; +var total = 0; +for (key in lastLevelCompleted) { + sorted.push({name: key, count: lastLevelCompleted[key]}); + total += lastLevelCompleted[key]; +} +sorted.sort(function(a,b) { return b.count - a.count}); +for (var i = 0; i < sorted.length; i++) { + print(sorted[i].count + "\t" + (sorted[i].count / total * 100).toFixed(2) + "%\t" + sorted[i].name); +} + +print("Runtime: " + (new Date() - startTime) + "ms"); diff --git a/scripts/analytics/mongodb/queries/SignupConversions.js b/scripts/analytics/mongodb/queries/SignupConversions.js new file mode 100644 index 000000000..b2b15422b --- /dev/null +++ b/scripts/analytics/mongodb/queries/SignupConversions.js @@ -0,0 +1,29 @@ +// Print out signup conversions since a start Date + +var started = finished = 0.0 +var startDate = new ISODate("2014-12-07T00:00:00.000Z") +db['analytics.log.events'].aggregate( +[ + { $match : + { $and: [ + {created : { $gte: startDate}}, + {$or: [ {"event" : 'Started Signup'}, {"event" : 'Finished Signup'}]} + ]} + }, + { + $group : { + _id: "$event", + total: {$sum: 1} + } + }, + { $sort : { total : -1} } +] +).result.forEach( function (myDoc) { + //print(myDoc) + if (myDoc._id === "Started Signup") + started += myDoc.total; + else + finished += myDoc.total; +}) +print("Signups", started, finished); +print("Signup Conversion:", (finished / started * 100), "%"); diff --git a/scripts/analytics/mongodb/queries/SignupsPerDay.js b/scripts/analytics/mongodb/queries/SignupsPerDay.js new file mode 100644 index 000000000..5f65b1c9a --- /dev/null +++ b/scripts/analytics/mongodb/queries/SignupsPerDay.js @@ -0,0 +1,91 @@ +// Print out signup conversions per day + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// TODO: Dec 18th has more signups finished than started. Too good to be true. + +var match={ + "$match" : { + $or: [ {"event" : 'Started Signup'}, {"event" : 'Finished Signup'}] + } +}; + +proj1={"$project" : { + "_id" : 0, + "created" : 1, + "event" : 1, + "h" : { + "$hour" : "$created" + }, + "m" : { + "$minute" : "$created" + }, + "s" : { + "$second" : "$created" + }, + "ml" : { + "$millisecond" : "$created" + } + } +}; + +var proj2={"$project" : { + "_id" : 0, + "event" : 1, + "created" : { + "$subtract" : [ + "$created", + { + "$add" : [ + "$ml", + { + "$multiply" : [ + "$s", + 1000 + ] + }, + { + "$multiply" : [ + "$m", + 60, + 1000 + ] + }, + { + "$multiply" : [ + "$h", + 60, + 60, + 1000 + ] + } + ] + } + ] + } + } +}; +var group={"$group" : { + "_id" : { + "m" : "$event", + "d" : "$created" + }, + "count" : { + "$sum" : 1 + } + } +}; +var conversionsPerDay = {}; +var sort = {$sort: { "_id.d" : -1}}; +var cursor = db['analytics.log.events'].aggregate(match, proj1, proj2, group, sort); + +while (cursor.hasNext()) { + var myDoc = cursor.next(); + var key = myDoc._id.d.toDateString() + if (!conversionsPerDay[key]) conversionsPerDay[key] = {} + conversionsPerDay[key][myDoc._id.m] = myDoc.count; +} +for (key in conversionsPerDay) { + print(key + "\t" + conversionsPerDay[key]['Started Signup'] + "\t" + conversionsPerDay[key]['Finished Signup'] + "\t" + (conversionsPerDay[key]['Finished Signup'] / conversionsPerDay[key]['Started Signup'] * 100).toFixed(2) + "%"); +} \ No newline at end of file diff --git a/scripts/analytics/mongodb/queries/SubscriptionsPerDay.js b/scripts/analytics/mongodb/queries/SubscriptionsPerDay.js new file mode 100644 index 000000000..155cddc98 --- /dev/null +++ b/scripts/analytics/mongodb/queries/SubscriptionsPerDay.js @@ -0,0 +1,38 @@ +// Print out subscription counts bucketed by day and amount +// NOTE: created is a string and not an ISODate in the database + +// Usage: +// mongo <address>:<port>/coco <script file> -u <username> -p <password> + +// TODO: does not differeniate between new and recurring payments + +var match={ + "$match" : { + "stripe.subscriptionID" : { "$exists" : true } + } +}; +var proj0 = {"$project": { + "amount": 1, + "created": {"$substr" : ["$created", 0, 10]} +} +}; +var group={"$group" : { + "_id" : { + "m" : "$amount", + "d" : "$created" + }, + "count" : { + "$sum" : 1 + } + } +}; +var sort = {$sort: { "_id.d" : -1}}; +//db.payments.aggregate(match, proj0, proj1, proj2, group) +var cursor = db.payments.aggregate(match, proj0, group, sort); +while (cursor.hasNext()) { + var myDoc = cursor.next(); + print(myDoc._id.d, myDoc._id.m, myDoc.count); +} +//db.payments.aggregate(match, proj0, group, sort) +//db.payments.aggregate(match) +//db.payments.find() diff --git a/scripts/analytics/mongodb/queries/abForeshadowsLevels.js b/scripts/analytics/mongodb/queries/abForeshadowsLevels.js new file mode 100644 index 000000000..3127b61cc --- /dev/null +++ b/scripts/analytics/mongodb/queries/abForeshadowsLevels.js @@ -0,0 +1,50 @@ +// foreshadowsLevels A/B Results +// Test started 2015-01-29, ended 2015-02-26 +// Fixed some particle problems on 2015-02-02 +// Fixed more particle problems in old browsers on 2015-02-19 +// Final results: tests showed no difference in completion rates, but slight drops in level start rates for non-Chrome browsers, so we stopped doing it outside of Chrome. + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +load('abTestHelpers.js'); + +var scriptStartTime = new Date(); +try { + var startDay = '2015-02-20'; + log("Today is " + new Date().toISOString().substr(0, 10)); + log("Start day is " + startDay); + + var eventFunnel = ['Started Level', 'Saw Victory']; + var levelSlugs = ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'forgetful-gemsmith', 'kounter-kithwise', 'kithgard-gates', 'rich-forager', 'village-guard']; + + // getForeshadowsLevels + var testGroupFn = function (testGroupNumber) { + var group = testGroupNumber % 16 + return group >= 0 && group <= 7; + }; + + var funnelData = getFunnelData(startDay, eventFunnel, testGroupFn, levelSlugs); + + printFunnelData(funnelData, function (day, level, browser, group, started, finished, rate) { + if (day && level && browser && group) { + log(day + "\t" + group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (level && browser && group) { + log(level + "\t" + browser + "\t" + (browser.length < 8 ? "\t": "") + group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (level && group) { + log(level + "\t" + group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (group) { + log(group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + }); +} +catch(err) { + log("ERROR: " + err); + printjson(err); +} +finally { + log("Script runtime: " + (new Date() - scriptStartTime)); +} diff --git a/scripts/analytics/mongodb/queries/abGemPromptGroup.js b/scripts/analytics/mongodb/queries/abGemPromptGroup.js new file mode 100644 index 000000000..6f79d3544 --- /dev/null +++ b/scripts/analytics/mongodb/queries/abGemPromptGroup.js @@ -0,0 +1,45 @@ +// gemPromptGroup A/B Results +// Test started 2014-11-24, ended 2015-02-26 +// Final results: +// no-prompt 3789 gem shop shown, 62 purchased +// prompt 2658 gem shop shown, 78 purchased +// Decided prompt was better, so now always prompt. (Yay being nice.) + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +load('abTestHelpers.js'); + +var scriptStartTime = new Date(); +try { + var startDay = '2014-11-24'; + // startDay = '2015-01-15' + log("Today is " + new Date().toISOString().substr(0, 10)); + log("Start day is " + startDay); + + var eventFunnel = ['Started purchase', 'Finished gem purchase']; + + // getGemPromptGroup + var testGroupFn = function (testGroupNumber) { + var group = testGroupNumber % 8 + return group >= 0 && group <= 3 ? 'prompt' : 'no-prompt'; + }; + + var funnelData = getFunnelData(startDay, eventFunnel, testGroupFn); + + printFunnelData(funnelData, function (day, level, browser, group, started, finished, rate) { + if (day && level && browser && group) { + log(day + "\t" + group + "\t" + (group === 'prompt' ? "\t": "") + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (group) { + log(group + (group === 'prompt' ? "\t": "") + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + }); +} +catch(err) { + log("ERROR: " + err); + printjson(err); +} +finally { + log("Script runtime: " + (new Date() - scriptStartTime)); +} diff --git a/scripts/analytics/mongodb/queries/abLeaderboardsGroup.js b/scripts/analytics/mongodb/queries/abLeaderboardsGroup.js new file mode 100644 index 000000000..c0ff8cda8 --- /dev/null +++ b/scripts/analytics/mongodb/queries/abLeaderboardsGroup.js @@ -0,0 +1,51 @@ +// leaderboardsGroup A/B Results +// Test started 2015-01-30, ended 2015-02-26 +// Final results: no differences in level starts or completions. Perhaps they affect playtime or purchases, but harder to say. At least they don't hurt, so wejust turned them on for everyone always. + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +load('abTestHelpers.js'); + +var scriptStartTime = new Date(); +try { + var startDay = '2015-02-07'; + log("Today is " + new Date().toISOString().substr(0, 10)); + log("Start day is " + startDay); + + var eventFunnel = ['Started Level', 'Saw Victory']; + var levelSlugs = ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'forgetful-gemsmith', 'kounter-kithwise', 'kithgard-gates', 'rich-forager', 'village-guard']; + + // getLeaderboardsGroup + var testGroupFn = function (testGroupNumber) { + var group = testGroupNumber % 64; + if (group < 16) return 'always'; + if (group < 32) return 'early'; + if (group < 48) return 'late'; + return 'never'; + }; + + var funnelData = getFunnelData(startDay, eventFunnel, testGroupFn, levelSlugs); + + printFunnelData(funnelData, function (day, level, browser, group, started, finished, rate) { + if (day && level && browser && group) { + log(day + "\t" + group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (level && browser && group) { + log(level + "\t" + browser + "\t" + (browser.length < 8 ? "\t": "") + group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (level && group) { + log(level + "\t" + group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (group) { + log(group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + }); +} +catch(err) { + log("ERROR: " + err); + printjson(err); +} +finally { + log("Script runtime: " + (new Date() - scriptStartTime)); +} diff --git a/scripts/analytics/mongodb/queries/abShowsPortal.js b/scripts/analytics/mongodb/queries/abShowsPortal.js new file mode 100644 index 000000000..cc687f39b --- /dev/null +++ b/scripts/analytics/mongodb/queries/abShowsPortal.js @@ -0,0 +1,66 @@ +// showsPortal A/B Results +// Test started 2015-02-05, ended 2015-02-26 +// Final results: seems to help people get to the later levels, and at least definitely doesn't hurt: +// dungeons-of-kithgard false 64605 49956 77.33 +// dungeons-of-kithgard true 65380 50590 77.38 +// gems-in-the-deep false 45339 40788 89.96 +// gems-in-the-deep true 45922 41455 90.27 +// kithgard-gates false 7415 6904 93.11 +// kithgard-gates true 7783 7249 93.14 +// kounter-kithwise true 95 92 96.84 +// kounter-kithwise false 86 77 89.53 +// rich-forager false 1067 822 77.04 +// rich-forager true 1111 834 75.07 +// shadow-guard false 38089 35239 92.52 +// shadow-guard true 38774 35975 92.78 +// the-mighty-sand-yak true 505 400 79.21 +// the-mighty-sand-yak false 425 329 77.41 +// +// Group totals: +// false 157026 134115 85.41 +// true 159570 136595 85.60 + + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +load('abTestHelpers.js'); + +var scriptStartTime = new Date(); +try { + var startDay = '2015-02-05' + log("Today is " + new Date().toISOString().substr(0, 10)); + log("Start day is " + startDay); + + var eventFunnel = ['Started Level', 'Saw Victory']; + var levelSlugs = ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'kounter-kithwise', 'kithgard-gates', 'rich-forager', 'the-mighty-sand-yak']; + + // getShowsPortal + var testGroupFn = function (testGroupNumber) { + return testGroupNumber < 128; + } + + var funnelData = getFunnelData(startDay, eventFunnel, testGroupFn, levelSlugs); + + printFunnelData(funnelData, function (day, level, browser, group, started, finished, rate) { + if (day && level && browser && group) { + log(day + "\t" + group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (level && browser && group) { + log(level + "\t" + browser + "\t" + (browser.length < 8 ? "\t": "") + group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (level && group) { + log(level + "\t" + group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + else if (group) { + log(group + "\t" + started + "\t" + finished + "\t" + rate.toFixed(2)); + } + }); +} +catch(err) { + log("ERROR: " + err); + printjson(err); +} +finally { + log("Script runtime: " + (new Date() - scriptStartTime)); +} diff --git a/scripts/analytics/mongodb/queries/abTestHelpers.js b/scripts/analytics/mongodb/queries/abTestHelpers.js new file mode 100644 index 000000000..cfb975445 --- /dev/null +++ b/scripts/analytics/mongodb/queries/abTestHelpers.js @@ -0,0 +1,310 @@ +// A/B test helper functions +// Loaded from ab*.js ab test result scripts +// Main APIs are getFunnelData() and printFunnelData() + +// TODO: use levelSlugs in query if available +// TODO: Stop looking up testGroupNumber when test group data is available in analytics.log.events +// TODO: These are super slow, need to aggregate into analytics.perdays collection + +var browserCountPrintThreshold = 1000; + +var analyticsStringCache = {}; +var analyticsStringIDCache = {}; + +// *** Helper functions *** + +function log(str) { + print(new Date().toISOString() + " " + str); +} + +function objectIdWithTimestamp(timestamp) { + // Convert string date to Date object (otherwise assume timestamp is a date) + if (typeof(timestamp) == 'string') timestamp = new Date(timestamp); + // Convert date object to hex seconds since Unix epoch + var hexSeconds = Math.floor(timestamp/1000).toString(16); + // Create an ObjectId with that hex timestamp + var constructedObjectId = ObjectId(hexSeconds + "0000000000000000"); + return constructedObjectId +} + +function getAnalyticsString(strID) { + if (analyticsStringCache[strID]) return analyticsStringCache[strID]; + var doc = db['analytics.strings'].findOne({_id: strID}); + if (doc) { + analyticsStringCache[strID] = doc.v; + return analyticsStringCache[strID]; + } + throw new Error("ERROR: Did not find analytics.strings insert for: " + str); +} + +function getAnalyticsStringID(str) { + if (analyticsStringIDCache[str]) return analyticsStringIDCache[str]; + var doc = db['analytics.strings'].findOne({v: str}); + if (doc) { + analyticsStringIDCache[str] = doc._id; + return analyticsStringIDCache[str]; + } + throw new Error("ERROR: Did not find analytics.strings insert for: " + str); +} + +function getFunnelData(startDay, eventFunnel, testGroupFn, levelSlugs) { + if (!startDay || !eventFunnel || eventFunnel.length === 0 || !testGroupFn) return {}; + + // log('getFunnelData:'); + // log(startDay); + // log(eventFunnel); + + var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var queryParams = {$and: [{_id: {$gte: startObj}},{"event": {$in: eventFunnel}}]}; + var cursor = db['analytics.log.events'].find(queryParams); + + log("Fetching events.."); + // Map ordering: level, user, event, day + var levelUserEventMap = {}; + var levelSessions = []; + var users = []; + while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var day = created.substring(0, 10); + var event = doc.event; + var properties = doc.properties; + var user = doc.user.valueOf(); + var level = 'n/a'; + var ls = null; + + // TODO: Switch to properties.levelID for 'Saw Victory' + if (event === 'Saw Victory' && properties.level) level = properties.level.toLowerCase().replace(/ /g, '-'); + else if (properties.levelID) level = properties.levelID + + if (levelSlugs && levelSlugs.indexOf(level) < 0) continue; + + if (properties && properties.ls) { + ls = properties.ls.valueOf(); + levelSessions.push(properties.ls); + } + + users.push(ObjectId(user)); + + if (!levelUserEventMap[level]) levelUserEventMap[level] = {}; + if (!levelUserEventMap[level][user]) levelUserEventMap[level][user] = {}; + if (!levelUserEventMap[level][user][event] + || levelUserEventMap[level][user][event]['day'].localeCompare(day) > 0) { + levelUserEventMap[level][user][event] = {day: day}; + if (ls) { + levelUserEventMap[level][user][event]['ls'] = ls; + } + } + } + // printjson(levelUserEventMap); + // printjson(users); + + log("Fetching users.."); + var userGroupMap = {}; + cursor = db['users'].find({_id : {$in: users}}); + while (cursor.hasNext()) { + var doc = cursor.next(); + var user = doc._id.valueOf(); + userGroupMap[user] = testGroupFn(doc.testGroupNumber); + } + // printjson(userGroupMap); + + log("Fetching level sessions.."); + var lsBrowserMap = {}; + var userBrowserMap = {}; + cursor = db['level.sessions'].find({_id : {$in: levelSessions}}); + while (cursor.hasNext()) { + var doc = cursor.next(); + var user = doc._id.valueOf(); + var browser = doc.browser; + var browserInfo = ''; + if (browser && browser.platform) { + browserInfo += browser.platform; + } + if (browser && browser.name) { + browserInfo += browser.name; + } + if (browserInfo.length > 0) { + lsBrowserMap[doc._id.valueOf()] = browserInfo; + userBrowserMap[user] = browserInfo; + } + } + // printjson(lsBrowserMap); + + log("Mapping data.."); + // Data: level, day, event + var levelDayGroupBrowserEventMap = {}; + for (level in levelUserEventMap) { + for (user in levelUserEventMap[level]) { + var group = userGroupMap[user]; + var browser = userBrowserMap[user] || 'unknown'; + + // Find first event date + var funnelStartDay = null; + var funnelStartBrowser = null; + for (event in levelUserEventMap[level][user]) { + var day = levelUserEventMap[level][user][event]['day']; + var ls = levelUserEventMap[level][user][event]['ls']; + if (lsBrowserMap[ls]) { + browser = lsBrowserMap[ls]; + } + if (!levelDayGroupBrowserEventMap[level]) levelDayGroupBrowserEventMap[level] = {}; + if (!levelDayGroupBrowserEventMap[level][day]) levelDayGroupBrowserEventMap[level][day] = {}; + if (!levelDayGroupBrowserEventMap[level][day][group]) levelDayGroupBrowserEventMap[level][day][group] = {}; + if (!levelDayGroupBrowserEventMap[level][day][group][browser]) { + levelDayGroupBrowserEventMap[level][day][group][browser] = {}; + } + if (!levelDayGroupBrowserEventMap[level][day][group][browser][event]) { + levelDayGroupBrowserEventMap[level][day][group][browser][event] = 0; + } + if (eventFunnel[0] === event) { + // First event gets attributed to current date + levelDayGroupBrowserEventMap[level][day][group][browser][event]++; + funnelStartDay = day; + funnelStartBrowser = browser; + break; + } + } + + if (funnelStartDay) { + // Add remaining funnel steps/events to first step's date + for (event in levelUserEventMap[level][user]) { + if (!levelDayGroupBrowserEventMap[level][funnelStartDay][group][funnelStartBrowser]) { + levelDayGroupBrowserEventMap[level][funnelStartDay][group][funnelStartBrowser] = {}; + } + if (!levelDayGroupBrowserEventMap[level][funnelStartDay][group][funnelStartBrowser][event]) { + levelDayGroupBrowserEventMap[level][funnelStartDay][group][funnelStartBrowser][event] = 0; + } + if (eventFunnel[0] !== event) { + levelDayGroupBrowserEventMap[level][funnelStartDay][group][funnelStartBrowser][event]++; + } + } + // Zero remaining funnel events + for (var i = 1; i < eventFunnel.length; i++) { + var event = eventFunnel[i]; + if (!levelDayGroupBrowserEventMap[level][funnelStartDay][group][funnelStartBrowser][event]) { + levelDayGroupBrowserEventMap[level][funnelStartDay][group][funnelStartBrowser][event] = 0; + } + if (!levelDayGroupBrowserEventMap[level][funnelStartDay][group][funnelStartBrowser][event]) { + levelDayGroupBrowserEventMap[level][funnelStartDay][group][funnelStartBrowser][event] = 0; + } + } + } + // Else no start event in this date range + } + } + // printjson(levelDayGroupBrowserEventMap); + + log("Building results.."); + var funnelData = []; + for (level in levelDayGroupBrowserEventMap) { + for (day in levelDayGroupBrowserEventMap[level]) { + for (group in levelDayGroupBrowserEventMap[level][day]) { + for (browser in levelDayGroupBrowserEventMap[level][day][group]) { + var started = 0; + var finished = 0; + for (event in levelDayGroupBrowserEventMap[level][day][group][browser]) { + if (event === eventFunnel[0]) { + started = levelDayGroupBrowserEventMap[level][day][group][browser][event]; + } + else if (event === eventFunnel[eventFunnel.length - 1]) { + finished = levelDayGroupBrowserEventMap[level][day][group][browser][event]; + } + } + funnelData.push({ + level: level, + day: day, + group: group, + browser: browser, + started: started, + finished: finished + }); + } + } + } + } + + log("Sorting results.."); + funnelData.sort(function (a,b) { + if (a.level !== b.level) { + return a.level < b.level ? -1 : 1; + } + else if (a.day !== b.day) { + return a.day < b.day ? -1 : 1; + } + else if (a.browser !== b.browser) { + return a.browser < b.browser ? -1 : 1; + } + return a.group < b.group ? -1 : 1; + }); + + return funnelData; +} + +function printFunnelData(funnelData, printFn) { + log("Day\t\tGroup\t\tStarted\tFinished\tCompletion Rate"); + var levelBrowserGroupCounts = {}; + var levelGroupCounts = {}; + var groupCounts = {}; + for (var i = 0; i < funnelData.length; i++) { + var level = funnelData[i].level; + var day = funnelData[i].day; + var browser = funnelData[i].browser; + var group = funnelData[i].group; + var started = funnelData[i].started; + var finished = funnelData[i].finished; + var rate = started > 0 ? finished / started * 100 : 0.0; + printFn(day, level, browser, group, started, finished, rate); + + if (!levelBrowserGroupCounts[level]) levelBrowserGroupCounts[level] = {}; + if (!levelBrowserGroupCounts[level][browser]) levelBrowserGroupCounts[level][browser] = {}; + if (!levelBrowserGroupCounts[level][browser][group]) { + levelBrowserGroupCounts[level][browser][group] = {started: 0, finished: 0}; + } + levelBrowserGroupCounts[level][browser][group]['started'] += started; + levelBrowserGroupCounts[level][browser][group]['finished'] += finished; + + if (!levelGroupCounts[level]) levelGroupCounts[level] = {}; + if (!levelGroupCounts[level][group]) levelGroupCounts[level][group] = {started: 0, finished: 0}; + levelGroupCounts[level][group]['started'] += started; + levelGroupCounts[level][group]['finished'] += finished; + + if (!groupCounts[group]) groupCounts[group] = {started: 0, finished: 0}; + groupCounts[group]['started'] += started; + groupCounts[group]['finished'] += finished; + } + + log(""); + log("Browser totals:"); + for (level in levelBrowserGroupCounts) { + for (browser in levelBrowserGroupCounts[level]) { + for (group in levelBrowserGroupCounts[level][browser]) { + var started = levelBrowserGroupCounts[level][browser][group].started; + if (started < browserCountPrintThreshold) continue; + var finished = levelBrowserGroupCounts[level][browser][group].finished; + var rate = started > 0 ? finished / started * 100 : 0.0; + printFn(null, level, browser, group, started, finished, rate); + } + } + } + + log(""); + log("Level totals:"); + for (level in levelGroupCounts) { + for (group in levelGroupCounts[level]) { + var started = levelGroupCounts[level][group].started; + var finished = levelGroupCounts[level][group].finished; + var rate = started > 0 ? finished / started * 100 : 0.0; + printFn(null, level, null, group, started, finished, rate); + } + } + + log(""); + log("Group totals:"); + for (group in groupCounts) { + var started = groupCounts[group].started; + var finished = groupCounts[group].finished; + var rate = started > 0 ? finished / started * 100 : 0.0; + printFn(null, null, null, group, started, finished, rate); + } +} diff --git a/scripts/analytics/mongodb/queries/aggregateEventTotals.js b/scripts/analytics/mongodb/queries/aggregateEventTotals.js new file mode 100644 index 000000000..f757256ad --- /dev/null +++ b/scripts/analytics/mongodb/queries/aggregateEventTotals.js @@ -0,0 +1,21 @@ +// Print out event totals + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +var cursor = db['analytics.log.events'].aggregate( +[ + { $match : {}}, + { + $group : { + _id: "$event", + total: {$sum: 1} + } + }, + { $sort : { total : -1} } +]); + +while (cursor.hasNext()) { + var myDoc = cursor.next(); + print(myDoc.total + "\t" + myDoc._id) +} diff --git a/scripts/analytics/mongodb/queries/averageLevelPlaytimes.js b/scripts/analytics/mongodb/queries/averageLevelPlaytimes.js new file mode 100644 index 000000000..0a7f18330 --- /dev/null +++ b/scripts/analytics/mongodb/queries/averageLevelPlaytimes.js @@ -0,0 +1,136 @@ +// Average level playtimes by campaign + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// NOTE: faster to use find() instead of aggregate() +// NOTE: faster to ask for one level at a time. also keeps levels in campaign order + +// Excluded for one reason or another +// Some relevant code: https://github.com/codecombat/codecombat/blob/master/app/views/play/CampaignView.coffee#L281-L292 +var excludedLevels = ['deadly-dungeon-rescue', 'kithgard-brawl', 'cavern-survival', 'kithgard-mastery', 'destroying-angel', 'kithgard-apprentice', 'wild-horses', 'lost-viking', 'forest-flower-grove', 'boulder-woods', 'the-trials']; + +var scriptStartTime = new Date(); +var startDay = '2015-05-10'; +var endDay = '2015-06-11'; + +log("Dates: " + startDay + " to " + endDay); + +// Print out playtimes for each campaign +var campaigns = getCampaigns(); +for (var i = 0; i < campaigns.length; i++) { + var campaign = campaigns[i]; + // if (campaign.slug !== 'dungeon') continue; + print(campaign.slug + " (free)"); + var total = 0; + + for (var j = 0; j < campaign.free.length; j++) { + var levelSlug = campaign.free[j]; + if (excludedLevels.indexOf(levelSlug) >= 0) continue; + var data = getPlaytimes([levelSlug]); + print(data[levelSlug].average + "\t" + data[levelSlug].count + "\t" + levelSlug); + total += data[levelSlug]; + } + // print(parseInt(total/60/60) + "\t\t total hours"); + total = 0; + + print(campaign.slug + " (paid)"); + for (var j = 0; j < campaign.paid.length; j++) { + var levelSlug = campaign.paid[j]; + if (excludedLevels.indexOf(levelSlug) >= 0) continue; + var data = getPlaytimes([levelSlug]); + if (data[levelSlug]) { + print(data[levelSlug].average + "\t" + data[levelSlug].count + "\t" + levelSlug); + total += data[levelSlug]; + } + else { + print("0\t0\t" + levelSlug); + } + } + // print(parseInt(total/60/60) + "\t\t total hours"); + total = 0; + + print(campaign.slug + " (replayable)"); + for (var j = 0; j < campaign.replayable.length; j++) { + var levelSlug = campaign.replayable[j]; + if (excludedLevels.indexOf(levelSlug) >= 0) continue; + var data = getPlaytimes([levelSlug]); + print(data[levelSlug].average + "\t" + data[levelSlug].count + "\t" + levelSlug); + total += data[levelSlug]; + } + // print(parseInt(total/60/60) + "\t\t total hours"); + + // break; +} + +log("Script runtime: " + (new Date() - scriptStartTime)); + +function log(str) { + print(new Date().toISOString() + " " + str); +} + +function objectIdWithTimestamp(timestamp) { + // Convert string date to Date object (otherwise assume timestamp is a date) + if (typeof(timestamp) == 'string') timestamp = new Date(timestamp); + // Convert date object to hex seconds since Unix epoch + var hexSeconds = Math.floor(timestamp/1000).toString(16); + // Create an ObjectId with that hex timestamp + var constructedObjectId = ObjectId(hexSeconds + "0000000000000000"); + return constructedObjectId +} + +function getCampaigns() { + var campaigns = []; + var cursor = db.campaigns.find({}, {slug: 1, levels: 1}); + var allFree = 0; + var allpaid = 0; + while (cursor.hasNext()) { + var doc = cursor.next(); + if (doc.slug === 'auditions') continue; + var campaign = {slug: doc.slug, free: [], paid: [], replayable: []}; + for (var levelID in doc.levels) { + if (doc.levels[levelID].replayable) { + campaign.replayable.push(doc.levels[levelID].slug); + } + else if (doc.levels[levelID].requiresSubscription) { + campaign.paid.push(doc.levels[levelID].slug); + } + else { + campaign.free.push(doc.levels[levelID].slug); + } + } + campaigns.push(campaign); + } + return campaigns; +} + +function getPlaytimes(levelSlugs) { + var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var endObj = objectIdWithTimestamp(ISODate(endDay + "T00:00:00.000Z")) + var cursor = db['level.sessions'].find({ + $and: + [ + {"state.complete": true}, + {"playtime": {$gt: 0}}, + {levelID: {$in: levelSlugs}}, + {_id: {$gte: startObj}}, + {_id: {$lt: endObj}} + ] + }); + + var playtimes = {}; + while (cursor.hasNext()) { + var myDoc = cursor.next(); + var levelID = myDoc.levelID; + if (!playtimes[levelID]) playtimes[levelID] = []; + playtimes[levelID].push(myDoc.playtime); + } + + var data = {}; + for (levelID in playtimes) { + var total = playtimes[levelID].reduce(function(a, b) {return a + b;}); + data[levelID] = {count: playtimes[levelID].length, total: total}; + data[levelID]['average'] = parseInt(total / playtimes[levelID].length); + } + return data; +} diff --git a/scripts/analytics/mongodb/queries/campaignLevelCounts.js b/scripts/analytics/mongodb/queries/campaignLevelCounts.js new file mode 100644 index 000000000..7a1fa697d --- /dev/null +++ b/scripts/analytics/mongodb/queries/campaignLevelCounts.js @@ -0,0 +1,28 @@ +// Print out campaign level counts + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +print("free premium all campaign"); +var cursor = db.campaigns.find({}, {slug: 1, levels: 1}); +var allFree = 0; +var allPremium = 0; +while (cursor.hasNext()) { + var doc = cursor.next(); + if (doc.slug === 'auditions') continue; + var free = 0; + var premium = 0; + for (var levelID in doc.levels) { + if (doc.levels[levelID].requiresSubscription) { + premium++; + } + else { + free++; + } + } + print(free + " " + premium + " " + (free + premium) + " " + doc.slug); + + allFree += free; + allPremium += premium; +} +print(allFree + " " + allPremium + " " + (allFree + allPremium) + " overall"); diff --git a/scripts/analytics/mongodb/queries/campaignRates.js b/scripts/analytics/mongodb/queries/campaignRates.js new file mode 100644 index 000000000..a5592bdc1 --- /dev/null +++ b/scripts/analytics/mongodb/queries/campaignRates.js @@ -0,0 +1,263 @@ +// Print out campaign drop-off rates +// Drop off: last started or finished level event +// Adjust startDate below for different timeframe than last 7 days. + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// Ignores the order at which levels are completed +// Ignores level skipping + +// TODO: Is our overall drop-off rate correct? +// TODO: What's the right time frame for this data? + +// TODO: Calculate completion rates per-level, and campaign overall + +var today = new Date(); +today = today.toISOString().substr(0, 10); +print("Today is " + today); + +var todayMinus6 = new Date(); +todayMinus6.setUTCDate(todayMinus6.getUTCDate() - 6); +var startDate = todayMinus6.toISOString().substr(0, 10) + "T00:00:00.000Z"; +// startDate = "2014-12-31T00:00:00.000Z"; +print("Start date is " + startDate) +// var endDate = "2015-01-06T00:00:00.000Z"; +// print("End date is " + endDate) + +function objectIdWithTimestamp(timestamp) +{ + // Convert string date to Date object (otherwise assume timestamp is a date) + if (typeof(timestamp) == 'string') timestamp = new Date(timestamp); + // Convert date object to hex seconds since Unix epoch + var hexSeconds = Math.floor(timestamp/1000).toString(16); + // Create an ObjectId with that hex timestamp + var constructedObjectId = ObjectId(hexSeconds + "0000000000000000"); + return constructedObjectId +} + +var cursor = db['analytics.log.events'].find({ + $and: [ + {_id: {$gte: objectIdWithTimestamp(ISODate(startDate))}}, + {$or: [ {"event" : 'Started Level'}, {"event" : 'Saw Victory'}]} + ] +}); + +var longestLevelName = -1; + + +// Copied from WorldMapView +var dungeonLevels = [ + 'dungeons-of-kithgard', + 'gems-in-the-deep', + 'shadow-guard', + 'kounter-kithwise', + 'crawlways-of-kithgard', + 'forgetful-gemsmith', + 'true-names', + 'favorable-odds', + 'the-raised-sword', + 'haunted-kithmaze', + 'riddling-kithmaze', + 'descending-further', + 'the-second-kithmaze', + 'dread-door', + 'known-enemy', + 'master-of-names', + 'lowly-kithmen', + 'closing-the-distance', + 'tactical-strike', + 'the-final-kithmaze', + 'the-gauntlet', + 'kithgard-gates', + 'cavern-survival' +]; + +var forestLevels = [ + 'defense-of-plainswood', + 'winding-trail', + 'patrol-buster', + 'endangered-burl', + 'village-guard', + 'thornbush-farm', + 'back-to-back', + 'ogre-encampment', + 'woodland-cleaver', + 'shield-rush', + 'peasant-protection', + 'munchkin-swarm', + 'munchkin-harvest', + 'swift-dagger', + 'shrapnel', + 'arcane-ally', + 'touch-of-death', + 'bonemender', + 'coinucopia', + 'copper-meadows', + 'drop-the-flag', + 'deadly-pursuit', + 'rich-forager', + 'siege-of-stonehold', + 'multiplayer-treasure-grove', + 'dueling-grounds' +]; + +var desertLevels = [ + 'the-dunes', + 'the-mighty-sand-yak', + 'oasis', + 'sarven-road', + 'sarven-gaps', + 'thunderhooves', + 'medical-attention', + 'minesweeper', + 'sarven-sentry', + 'keeping-time', + 'hoarding-gold', + 'decoy-drill', + 'yakstraction', + 'sarven-brawl' +]; + +var campaigns = { + 'dungeon': dungeonLevels, + 'forest': forestLevels, + 'desert': desertLevels +}; + +// Bucketize events by user +print("Getting event data..."); +var userProgression = {}; +var userLevelEventMap = {}; // Only want unique users per-level/event +while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc.created; + var event = doc.event; + if (event === 'Saw Victory') var level = doc.properties.level.toLowerCase().replace(/ /g, '-'); + else var level = doc.properties.levelID + if (level) { + if (level.length > longestLevelName) longestLevelName = level.length; + var user = doc.user.valueOf(); + if (!userLevelEventMap[user]) userLevelEventMap[user] = {}; + if (!userLevelEventMap[user][level]) userLevelEventMap[user][level] = {}; + if (!userLevelEventMap[user][level][event]) { + userLevelEventMap[user][level][event] = true; + if (!userProgression[user]) userProgression[user] = []; + userProgression[user].push({ + created: created, + event: event, + level: level + }); + } + } +} +longestLevelName += 2; + +print("Processing data..."); + +// Order user progression by created +for (user in userProgression) userProgression[user].sort(function (a,b) {return a.created < b.created ? -1 : 1}); + +// Per-level start/drop/finish/drop +var levelProgression = {}; +for (user in userProgression) { + for (var i = 0; i < userProgression[user].length; i++) { + var event = userProgression[user][i].event; + var level = userProgression[user][i].level; + if (!levelProgression[level]) { + levelProgression[level] = { + started: 0, + startDropped: 0, + finished: 0, + finishDropped: 0 + }; + } + if (event === 'Started Level') { + levelProgression[level].started++; + if (i === userProgression[user].length - 1) levelProgression[level].startDropped++; + } + else if (event === 'Saw Victory') { + levelProgression[level].finished++; + if (i === userProgression[user].length - 1) levelProgression[level].finishDropped++; + } + } +} + + +// Put in campaign order +// Calculate overall campaign stats +var campaignRates = {}; +for (level in levelProgression) { + for (campaign in campaigns) { + if (campaigns[campaign].indexOf(level) >= 0) { + var started = levelProgression[level].started; + var startDropped = levelProgression[level].startDropped; + var finished = levelProgression[level].finished; + var finishDropped = levelProgression[level].finishDropped; + if (!campaignRates[campaign]) { + campaignRates[campaign] = { levels: [], overall: { + started: 0, + startDropped: 0, + finished: 0, + finishDropped: 0 + }}; + } + campaignRates[campaign].levels.push({ + level: level, + started: started, + startDropped: startDropped, + finished: finished, + finishDropped: finishDropped + }); + campaignRates[campaign].overall.started += started; + campaignRates[campaign].overall.finished += finished; + campaignRates[campaign].overall.startDropped += startDropped; + + // Only finishDropped if on last level in campaign + if (campaigns[campaign].indexOf(level) === campaigns[campaign].length - 1) { + campaignRates[campaign].overall.finishDropped += finishDropped; + } + else { + campaignRates[campaign].overall.startDropped += finishDropped; + } + break; + } + } +} + +// Sort level data by campaign order +for (campaign in campaignRates) { + campaignRates[campaign].levels.sort(function(a, b) { + if (campaigns[campaign].indexOf(a.level) < campaigns[campaign].indexOf(b.level)) return -1; + return 1; + }); +} + + +print("\nCampaign drop off rates"); +print("Where do players stop playing?"); +print("Drop-off point: last start or finish level event."); +print("Columns: level, started the level, left after starting, finished level, left after finishing level"); + +for (campaign in campaigns) { + print("\n" + campaign); + var level = "level"; + var levelSpacer = new Array(longestLevelName - level.length).join(' '); + print(level + levelSpacer + "started\tdropped\t\tfinished dropped\tcompletion"); + for (var i = 0; i < campaignRates[campaign].levels.length; i++) { + var level = campaignRates[campaign].levels[i].level; + var started = campaignRates[campaign].levels[i].started; + var startDropped = campaignRates[campaign].levels[i].startDropped; + var finished = campaignRates[campaign].levels[i].finished; + var finishDropped = campaignRates[campaign].levels[i].finishDropped; + var levelSpacer = new Array(longestLevelName - level.length).join(' '); + print(level + levelSpacer + started + "\t" + (started < 100 ? "\t" : "") + startDropped + "\t" + (startDropped / started * 100).toFixed(2) + "%\t" + finished + "\t" + finishDropped + "\t" + (finishDropped / finished * 100).toFixed(2) + "%" + "\t" + (finished / started * 100).toFixed(2) + "%"); + } + // var level = 'Overall'; + // var started = campaignRates[campaign].overall.started; + // var startDropped = campaignRates[campaign].overall.startDropped; + // var finished = campaignRates[campaign].overall.finished; + // var finishDropped = campaignRates[campaign].overall.finishDropped; + // var levelSpacer = new Array(longestLevelName - level.length).join(' '); + // print(level + levelSpacer + started + "\t" + (started < 100 ? "\t" : "") + startDropped + "\t" + (startDropped / started * 100).toFixed(2) + "%\t" + finished + "\t" + finishDropped + "\t" + (finishDropped / finished * 100).toFixed(2) + "%"); +} diff --git a/scripts/analytics/mongodb/queries/helpUsage.js b/scripts/analytics/mongodb/queries/helpUsage.js new file mode 100644 index 000000000..094ba9aff --- /dev/null +++ b/scripts/analytics/mongodb/queries/helpUsage.js @@ -0,0 +1,156 @@ +// Help button and video usage + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// What do we want to know? +// For each level, how many clicks, starts, finishes +// Individual users only counted once for a level/event combo + +try { + var scriptStartTime = new Date(); + var analyticsStringCache = {}; + + // Look at last 30 days, same as Mixpanel + var numDays = 30; + + var startDay = new Date(); + today = startDay.toISOString().substr(0, 10); + startDay.setUTCDate(startDay.getUTCDate() - numDays); + startDay = startDay.toISOString().substr(0, 10); + + log("Today is " + today); + log("Start day is " + startDay); + + var events = ['Problem alert help clicked', 'Spell palette help clicked', 'Start help video', 'Finish help video']; + + var helpData = getHelpData(startDay, events); + helpData.sort(function (a,b) { + var clickedA = a['Problem alert help clicked'] || 0; + clickedA += a['Spell palette help clicked'] || 0; + var clickedB = b['Problem alert help clicked'] || 0; + clickedB += b['Spell palette help clicked'] || 0; + return clickedA < clickedB ? 1 : -1; + }); + + log('Help Clicks\tVideo Starts\tStart Rate\tVideo Finishes\tFinish Rate\tLevel') + for(var i = 0; i < helpData.length; i++) { + var level = helpData[i].level; + var clicked = helpData[i]['Problem alert help clicked'] || 0; + clicked += helpData[i]['Spell palette help clicked'] || 0; + var started = helpData[i]['Start help video'] || 0; + var startRate = clicked > 0 ? started / clicked * 100 : 0.0; + var finished = helpData[i]['Finish help video'] || 0; + var finishRate = clicked > 0 ? finished / clicked * 100 : 0.0; + if (started > 1) { + log(clicked + '\t' + started + '\t' + startRate.toFixed(2) + '%\t' + finished + '\t' + finishRate.toFixed(2) + '%\t' + level); + } + } + + log("Script runtime: " + (new Date() - scriptStartTime)); +} +catch(err) { + log("ERROR: " + err); + printjson(err); +} + +// *** Helper functions *** + +function log(str) { + print(new Date().toISOString() + " " + str); +} + +function objectIdWithTimestamp(timestamp) { + // Convert string date to Date object (otherwise assume timestamp is a date) + if (typeof(timestamp) == 'string') timestamp = new Date(timestamp); + // Convert date object to hex seconds since Unix epoch + var hexSeconds = Math.floor(timestamp/1000).toString(16); + // Create an ObjectId with that hex timestamp + var constructedObjectId = ObjectId(hexSeconds + "0000000000000000"); + return constructedObjectId +} + +function getAnalyticsString(str) { + if (analyticsStringCache[str]) return analyticsStringCache[str]; + + // Find existing string + var doc = db['analytics.strings'].findOne({v: str}); + if (doc) { + analyticsStringCache[str] = doc._id; + return analyticsStringCache[str]; + } + + // TODO: Not sure we want to always insert strings here. + // // Insert string + // // http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/#auto-increment-optimistic-loop + // doc = {v: str}; + // while (true) { + // var cursor = db['analytics.strings'].find({}, {_id: 1}).sort({_id: -1}).limit(1); + // var seq = cursor.hasNext() ? cursor.next()._id + 1 : 1; + // doc._id = seq; + // var results = db['analytics.strings'].insert(doc); + // if (results.hasWriteError()) { + // if ( results.writeError.code == 11000 /* dup key */ ) continue; + // else throw new Error("ERROR: Unexpected error inserting data: " + tojson(results)); + // } + // break; + // } + // + // // Find new string entry + // doc = db['analytics.strings'].findOne({v: str}); + // if (doc) { + // analyticsStringCache[str] = doc._id; + // return analyticsStringCache[str]; + // } + throw new Error("ERROR: Did not find analytics.strings insert for: " + str); +} + +function getHelpData(startDay, events) { + if (!startDay || !events || events.length === 0) return {}; + + var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var queryParams = {$and: [{_id: {$gte: startObj}},{"event": {$in: events}}]}; + var cursor = db['analytics.log.events'].find(queryParams); + + // Map ordering: level, user, event + var levelUserEventMap = {}; + while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var event = doc.event; + var user = doc.user.valueOf(); + var properties = doc.properties; + var level = properties.level || properties.levelID; + + if (!levelUserEventMap[level]) levelUserEventMap[level] = {}; + if (!levelUserEventMap[level][user]) levelUserEventMap[level][user] = {}; + if (!levelUserEventMap[level][user][event]) levelUserEventMap[level][user][event] = 1; + } + // printjson(levelUserEventMap); + + // Data: level, event, count + var levelEventMap = {}; + for (level in levelUserEventMap) { + for (user in levelUserEventMap[level]) { + for (event in levelUserEventMap[level][user]) { + if (!levelEventMap[level]) levelEventMap[level] = {}; + if (!levelEventMap[level][event]) levelEventMap[level][event] = 0; + levelEventMap[level][event] += levelUserEventMap[level][user][event]; + } + } + } + // printjson(levelEventMap); + + helpData = []; + for (level in levelEventMap) { + var data = {level: level}; + for (event in levelEventMap[level]) { + data[event] = levelEventMap[level][event]; + } + for (var i = 0; i < events.length; i++) { + if (!data[events[i]]) data[events[i]] = 0 + } + helpData.push(data); + } + return helpData; +} diff --git a/scripts/analytics/mongodb/queries/helpVideoStylesABTest.js b/scripts/analytics/mongodb/queries/helpVideoStylesABTest.js new file mode 100644 index 000000000..203a722f2 --- /dev/null +++ b/scripts/analytics/mongodb/queries/helpVideoStylesABTest.js @@ -0,0 +1,658 @@ +// Evaluate help videos styles A/B test + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// What do we want to know? +// For a given style: +// - Video completion rates (Not too interesting unless each level has all styles available) +// - Video completion rates, per-level too +// - Watched another video +// - Level completion rates +// - Subscription coversion totals +// - TODO: Check guide opens after haunted-kithmaze + +// TODO: look at date ranges before and after 2nd prod deploy + +// 12:42am 12/18/14 PST - Intial production deploy completed +var testStartDate = '2014-12-18T08:42:00.000Z'; +// 12:29pm 12/18/14 PST - 2nd deploy w/ originals for dungeons-of-kithgard and second-kithmaze +// testStartDate = '2014-12-18T20:29:00.000Z'; +// Moved this date up to avoid prod deploy transitional data messing with us. +testStartDate = '2014-12-18T22:29:00.000Z'; + +// Only print the levels we have multiple styles for +var multiStyleLevels = ['dungeons-of-kithgard', 'haunted-kithmaze']; + +var g_videoEventCounts = {}; +function initVideoEventCounts() { + // Per-level/style event counts to use for comparison correction later + // We have a weird sampling problem that doesn't yield equal test buckets + + print("Querying for help video events..."); + var cursor = db['analytics.log.events'].find({ + $and: [ + {"created": { $gte: ISODate(testStartDate)}}, + {$or: [ + {"event": "Start help video"}, + {"event": "Finish help video"} + ]} + ] + }); + + while (cursor.hasNext()) { + var doc = cursor.next(); + var levelID = doc.properties.level; + var style = doc.properties.style; + var event = doc.event; + if (!g_videoEventCounts[levelID]) g_videoEventCounts[levelID] = {}; + if (!g_videoEventCounts[levelID][style]) g_videoEventCounts[levelID][style] = {}; + if (!g_videoEventCounts[levelID][style][event]) g_videoEventCounts[levelID][style][event] = 0; + g_videoEventCounts[levelID][style][event]++; + } + // printjson(g_videoEventCounts); +} + +function printVideoCompletionRates() { + print("Querying for help video events..."); + var videosCursor = db['analytics.log.events'].find({ + $and: [ + {"created": { $gte: ISODate(testStartDate)}}, + {$or : [ + {"event": "Start help video"}, + {"event": "Finish help video"} + ]} + ] + }); + + // print("Building video progression data..."); + // Build: <style><level><userID><event> counts + var videoProgression = {}; + while (videosCursor.hasNext()) { + var doc = videosCursor.next(); + var userID = doc.user.valueOf(); + var levelID = doc.properties.level; + var style = doc.properties.style; + var event = doc.event; + if (!videoProgression[style]) videoProgression[style] = {}; + if (!videoProgression[style][levelID]) videoProgression[style][levelID] = {}; + if (!videoProgression[style][levelID][userID]) videoProgression[style][levelID][userID] = {}; + if (!videoProgression[style][levelID][userID][event]) videoProgression[style][levelID][userID][event] = 0; + videoProgression[style][levelID][userID][event]++; + } + + // Overall per-style + // TODO: Not too useful unless we have all styles for each level + + // // print("Counting start/finish events per-style..."); + // // Calculate overall video style completion rates, agnostic of level + // // Build: <style><event>{<starts>, <finishes>} + // var styleCompletionCounts = {} + // for (style in videoProgression) { + // styleCompletionCounts[style] = {}; + // for (levelID in videoProgression[style]) { + // for (userID in videoProgression[style][levelID]) { + // for (event in videoProgression[style][levelID][userID]) { + // if (!styleCompletionCounts[style][event]) styleCompletionCounts[style][event] = 0; + // styleCompletionCounts[style][event] += videoProgression[style][levelID][userID][event]; + // } + // } + // } + // } + // + // // print("Sorting per-style completion rates..."); + // var styleCompletionRates = []; + // for (style in styleCompletionCounts) { + // var started = 0; + // var finished = 0; + // for (event in styleCompletionCounts[style]) { + // if (event === "Start help video") started += styleCompletionCounts[style][event]; + // else if (event === "Finish help video") finished += styleCompletionCounts[style][event]; + // else throw new Error("Unknown event " + event); + // } + // var data = { + // style: style, + // started: started, + // finished: finished + // }; + // if (finished > 0) data['rate'] = finished / started * 100; + // styleCompletionRates.push(data); + // } + // styleCompletionRates.sort(function(a,b) {return b['rate'] && a['rate'] ? b.rate - a.rate : 0;}); + // + // // print("Overall per-style completion rates:"); + // for (var i = 0; i < styleCompletionRates.length; i++) { + // var item = styleCompletionRates[i]; + // var msg = item.style + (item.style === 'edited' ? "\t\t" : "\t") + item.started + "\t" + item.finished; + // if (item['rate']) msg += "\t" + item.rate + "%"; + // print(msg); + // } + + // Style completion rates per-level + + // print("Counting start/finish events per-level and style..."); + var styleLevelCompletionCounts = {} + for (style in videoProgression) { + for (levelID in videoProgression[style]) { + if (!styleLevelCompletionCounts[levelID]) styleLevelCompletionCounts[levelID] = {}; + if (!styleLevelCompletionCounts[levelID][style]) styleLevelCompletionCounts[levelID][style] = {}; + for (userID in videoProgression[style][levelID]) { + for (event in videoProgression[style][levelID][userID]) { + if (!styleLevelCompletionCounts[levelID][style][event]) styleLevelCompletionCounts[levelID][style][event] = 0; + styleLevelCompletionCounts[levelID][style][event] += videoProgression[style][levelID][userID][event]; + } + } + } + } + + // print("Sorting per-level completion rates..."); + var styleLevelCompletionRates = []; + for (levelID in styleLevelCompletionCounts) { + for (style in styleLevelCompletionCounts[levelID]) { + var started = 0; + var finished = 0; + for (event in styleLevelCompletionCounts[levelID][style]) { + if (event === "Start help video") started += styleLevelCompletionCounts[levelID][style][event]; + else if (event === "Finish help video") finished += styleLevelCompletionCounts[levelID][style][event]; + else throw new Error("Unknown event " + event); + } + var data = { + level: levelID, + style: style, + started: started, + finished: finished + }; + if (finished > 0) data['rate'] = finished / started * 100; + styleLevelCompletionRates.push(data); + } + } + styleLevelCompletionRates.sort(function(a,b) { + if (a.level !== b.level) { + if (a.level < b.level) return -1; + else return 1; + } + return b['rate'] && a['rate'] ? b.rate - a.rate : 0; + }); + + print("Per-level style completion rates:"); + for (var i = 0; i < styleLevelCompletionRates.length; i++) { + var item = styleLevelCompletionRates[i]; + if (multiStyleLevels.indexOf(item.level) >= 0) { + var msg = item.level + "\t" + item.style + (item.style === 'edited' ? "\t\t" : "\t") + item.started + "\t" + item.finished; + if (item['rate']) msg += "\t" + item.rate.toFixed(2) + "%"; + print(msg); + } + } +} + +function printWatchedAnotherVideoRates() { + // How useful is a style/level in yielding more video starts + // Algorithm: + // 1. Fetch all start/finish video events after test start date + // 2. Create a per-userID dictionary of user event history arrays + // 3. Sort each user event history array in ascending order. Now we have a video watching history, per-user. + // 4. Walk through each user's history + // a. Increment global count for level/style/event, for each level/style event in past history. + // b. Save current entry in the past history. + // 5. Sort by ascending level name, descending started count + + // TODO: only attribute one start/finish per level to a user? + + print("Querying for help video events..."); + var videosCursor = db['analytics.log.events'].find({ + $and: [ + {"created": { $gte: ISODate(testStartDate)}}, + {$or : [ + {"event": "Start help video"}, + {"event": "Finish help video"} + ]} + ] + }); + + // print("Building per-user video progression data..."); + // Find video progression per-user + // Build: <userID>[sorted style/event/level/date events] + var videoProgression = {}; + while (videosCursor.hasNext()) { + var doc = videosCursor.next(); + var event = doc.event; + var userID = doc.user.valueOf(); + var created = doc.created + var levelID = doc.properties.level; + var style = doc.properties.style; + + if (!videoProgression[userID]) videoProgression[userID] = []; + videoProgression[userID].push({ + style: style, + level: levelID, + event: event, + created: created.toISOString() + }) + } + // printjson(videoProgression); + + // print("Sorting per-user video progression data..."); + for (userID in videoProgression) videoProgression[userID].sort(function (a,b) {return a.created < b.created ? -1 : 1}); + + // print("Building per-level/style additional watched videos.."); + var additionalWatchedVideos = {}; + for (userID in videoProgression) { + + // Walk user's history, and tally what preceded each historical entry + var userHistory = videoProgression[userID]; + // printjson(userHistory); + var previouslyWatched = {}; + for (var i = 0; i < userHistory.length; i++) { + + // Walk previously watched events, and attribute to correct additionally watched entry + var item = userHistory[i]; + var level = item.level; + var style = item.style; + var event = item.event; + var created = item.created; + for (previousLevel in previouslyWatched) { + for (previousStyle in previouslyWatched[previousLevel]) { + if (previousLevel === level) continue; + // For previous level and style, 'event' followed it + if (!additionalWatchedVideos[previousLevel]) additionalWatchedVideos[previousLevel] = {}; + if (!additionalWatchedVideos[previousLevel][previousStyle]) { + additionalWatchedVideos[previousLevel][previousStyle] = {}; + } + // TODO: care which video watched next? + if (!additionalWatchedVideos[previousLevel][previousStyle][event]) { + additionalWatchedVideos[previousLevel][previousStyle][event] = 0; + } + additionalWatchedVideos[previousLevel][previousStyle][event]++; + // if (previousLevel === 'the-second-kithmaze') { + // print("Followed the-second-kithmaze " + userID + " " + level + " " + event + " " + created); + // } + } + } + + // Add level/style to previouslyWatched for this user + if (!previouslyWatched[level]) previouslyWatched[level] = {}; + if (!previouslyWatched[level][style]) previouslyWatched[level][style] = true; + } + } + + // print("Sorting additional watched videos by started event counts..."); + var additionalWatchedVideoByStarted = []; + for (levelID in additionalWatchedVideos) { + for (style in additionalWatchedVideos[levelID]) { + var started = 0; + var finished = 0; + for (event in additionalWatchedVideos[levelID][style]) { + if (event === "Start help video") started += additionalWatchedVideos[levelID][style][event]; + else if (event === "Finish help video") finished += additionalWatchedVideos[levelID][style][event]; + else throw new Error("Unknown event " + event); + } + var data = { + level: levelID, + style: style, + started: started, + finished: finished, + startAgainRate: started / g_videoEventCounts[levelID][style]['Start help video'] * 100, + finishAgainRate: finished / g_videoEventCounts[levelID][style]['Finish help video'] * 100 + }; + additionalWatchedVideoByStarted.push(data); + } + } + additionalWatchedVideoByStarted.sort(function(a,b) { + if (a.level !== b.level) { + if (a.level < b.level) return -1; + else return 1; + } + return b.startAgainRate - a.startAgainRate; + }); + + print("Per-level additional videos watched:"); + print("For a given level and style, this is how many more videos were started and finished."); + print("Columns: level, style, started, finished, started again rate, finished again rate"); + for (var i = 0; i < additionalWatchedVideoByStarted.length; i++) { + var item = additionalWatchedVideoByStarted[i]; + if (multiStyleLevels.indexOf(item.level) >= 0) { + print(item.level + "\t" + item.style + (item.style === 'edited' ? "\t\t" : "\t") + item.started + "\t" + item.finished + "\t" + item.startAgainRate.toFixed(2) + "%\t" + item.finishAgainRate.toFixed(2) + "%"); + } + } +} + +function printLevelCompletionRates() { + // For a level/style, how many completed that same level + // For a level/style, how many levels were completed afterwards? + + // Find each started event, per user + print("Querying for help video events..."); + var eventsCursor = db['analytics.log.events'].find({ + $and: [ + {"created": { $gte: ISODate(testStartDate)}}, + {$or : [ + {"event": "Start help video"}, + {"event": "Saw Victory"} + ]} + ] + }); + + // print("Building per-user events progression data..."); + // Find event progression per-user + var eventsProgression = {}; + while (eventsCursor.hasNext()) { + var doc = eventsCursor.next(); + var event = doc.event; + var userID = doc.user.valueOf(); + var created = doc.created + var levelID = doc.properties.level; + var style = doc.properties.style; + if (event === 'Saw Victory') levelID = levelID.toLowerCase().replace(/ /g, '-'); + if (!eventsProgression[userID]) eventsProgression[userID] = []; + eventsProgression[userID].push({ + style: style, + level: levelID, + event: event, + created: created.toISOString() + }) + } + + // print("Sorting per-user events progression data..."); + for (userID in eventsProgression) eventsProgression[userID].sort(function (a,b) {return a.created < b.created ? -1 : 1}); + + // print("Building per-level/style levels completed.."); + var levelsCompletedCounts = {}; + var sameLevelCompletedCounts = {}; + for (userID in eventsProgression) { + + // Walk user's history, and tally what preceded each historical entry + var userHistory = eventsProgression[userID]; + var previouslyWatched = {}; + for (var i = 0; i < userHistory.length; i++) { + + // Walk previously watched events, and attribute to correct additionally watched entry + var item = userHistory[i]; + var level = item.level; + var style = item.style; + var event = item.event; + var created = item.created; + + if (event === 'Start help video') { + // Add level/style to previouslyWatched for this user + if (!previouslyWatched[level]) previouslyWatched[level] = {}; + if (!previouslyWatched[level][style]) previouslyWatched[level][style] = true; + + } + else if (event === 'Saw Victory') { + for (previousLevel in previouslyWatched) { + for (previousStyle in previouslyWatched[previousLevel]) { + if (previousLevel === level) { + if (!sameLevelCompletedCounts[previousLevel]) sameLevelCompletedCounts[previousLevel] = {}; + if (!sameLevelCompletedCounts[previousLevel][previousStyle]) { + sameLevelCompletedCounts[previousLevel][previousStyle] = 0; + } + sameLevelCompletedCounts[previousLevel][previousStyle]++; + } + // For previous level and style, Saw Victory followed it + if (!levelsCompletedCounts[previousLevel]) levelsCompletedCounts[previousLevel] = {}; + if (!levelsCompletedCounts[previousLevel][previousStyle]) { + levelsCompletedCounts[previousLevel][previousStyle] = 0; + } + levelsCompletedCounts[previousLevel][previousStyle]++; + } + } + } + else { + throw new Error("Unknown event " + event); + } + } + } + + // print("Sorting level completed counts..."); + var levelsCompletedSorted = []; + for (levelID in levelsCompletedCounts) { + for (style in levelsCompletedCounts[levelID]) { + var data = { + level: levelID, + style: style, + completed: levelsCompletedCounts[levelID][style], + completedPerPlayer: levelsCompletedCounts[levelID][style] / g_videoEventCounts[levelID][style]['Start help video'] + }; + levelsCompletedSorted.push(data); + } + } + levelsCompletedSorted.sort(function(a,b) { + if (a.level !== b.level) { + if (a.level < b.level) return -1; + else return 1; + } + return b.completedPerPlayer - a.completedPerPlayer; + }); + + print("Total levels completed after video watched:"); + print("Columns: level, style, levels completed, completed per player"); + for (var i = 0; i < levelsCompletedSorted.length; i++) { + var item = levelsCompletedSorted[i]; + if (multiStyleLevels.indexOf(item.level) >= 0) { + print(item.level + "\t" + item.style + (item.style === 'edited' ? "\t\t" : "\t") + item.completed + "\t" + item.completedPerPlayer.toFixed(2)); + } + } + + var sameLevelCompletedSorted = []; + for (levelID in sameLevelCompletedCounts) { + for (style in sameLevelCompletedCounts[levelID]) { + var data = { + level: levelID, + style: style, + completed: sameLevelCompletedCounts[levelID][style], + completionRate: sameLevelCompletedCounts[levelID][style] / g_videoEventCounts[levelID][style]['Start help video'] * 100 + }; + sameLevelCompletedSorted.push(data); + } + } + sameLevelCompletedSorted.sort(function(a,b) { + if (a.level !== b.level) { + if (a.level < b.level) return -1; + else return 1; + } + return b.completionRate - a.completionRate; + }); + + print("Same level completed after video watched:"); + print("Columns: level, style, same level completed, completion rate"); + for (var i = 0; i < sameLevelCompletedSorted.length; i++) { + var item = sameLevelCompletedSorted[i]; + if (multiStyleLevels.indexOf(item.level) >= 0) { + print(item.level + "\t" + item.style + (item.style === 'edited' ? "\t\t" : "\t") + item.completed + "\t" + item.completionRate.toFixed(2) + "%"); + } + } +} + +function printSubConversionTotals() { + // For a user, who started a video, did they subscribe afterwards? + + // Find each started event, per user + print("Querying for help video start events..."); + var eventsCursor = db['analytics.log.events'].find({ + $and: [ + {"created": { $gte: ISODate(testStartDate)}}, + {$or : [ + {"event": "Start help video"}, + {"event": "Finished subscription purchase"} + ]} + ] + }); + + // print("Building per-user events progression data..."); + // Find event progression per-user + var eventsProgression = {}; + while (eventsCursor.hasNext()) { + var doc = eventsCursor.next(); + var event = doc.event; + var userID = doc.user.valueOf(); + var created = doc.created + var levelID = doc.properties.level; + var style = doc.properties.style; + + if (!eventsProgression[userID]) eventsProgression[userID] = []; + eventsProgression[userID].push({ + style: style, + level: levelID, + event: event, + created: created.toISOString() + }) + } + + // print("Sorting per-user events progression data..."); + for (userID in eventsProgression) eventsProgression[userID].sort(function (a,b) {return a.created < b.created ? -1 : 1}); + + // print("Building per-level/style sub purchases.."); + // Build: <level><style><count> + var subPurchaseCounts = {}; + for (userID in eventsProgression) { + var history = eventsProgression[userID]; + for (var i = 0; i < history.length; i++) { + if (history[i].event === 'Finished subscription purchase') { + var item = i > 0 ? history[i - 1] : {level: 'unknown', style: 'unknown'}; + if (!subPurchaseCounts[item.level]) subPurchaseCounts[item.level] = {}; + if (!subPurchaseCounts[item.level][item.style]) subPurchaseCounts[item.level][item.style] = 0; + subPurchaseCounts[item.level][item.style]++; + } + } + } + + // print("Sorting per-level/style sub purchase counts..."); + var subPurchasesByTotal = []; + for (levelID in subPurchaseCounts) { + for (style in subPurchaseCounts[levelID]) { + subPurchasesByTotal.push({ + level: levelID, + style: style, + total: subPurchaseCounts[levelID][style] + }) + } + } + subPurchasesByTotal.sort(function (a,b) { + if (a.level !== b.level) return a.level < b.level ? -1 : 1; + return b.total - a.total; + }); + + print("Per-level/style following sub purchases:"); + print("Columns: level, style, following sub purchases."); + print("'unknown' means no preceding start help video event."); + for (var i = 0; i < subPurchasesByTotal.length; i++) { + var item = subPurchasesByTotal[i]; + if (multiStyleLevels.indexOf(item.level) >= 0) { + print(item.level + "\t" + item.style + (item.style === 'edited' ? "\t\t" : "\t") + item.total); + } + } +} + +function printHelpClicksPostHaunted() { + // For a level/style, how many completed that same level + // For a level/style, how many levels were completed afterwards? + + // Find each started event, per user + print("Querying for help video events..."); + var eventsCursor = db['analytics.log.events'].find({ + $and: [ + {"created": { $gte: ISODate(testStartDate)}}, + {$or : [ + {$and:[{"event": "Start help video"}, {"properties.level": 'haunted-kithmaze'}]}, + {"event": "Problem alert help clicked"}, + {"event": "Spell palette help clicked"}, + ]} + ] + }); + + // print("Building per-user events progression data..."); + // Find event progression per-user + var eventsProgression = {}; + while (eventsCursor.hasNext()) { + var doc = eventsCursor.next(); + var event = doc.event; + var userID = doc.user.valueOf(); + var created = doc.created + var levelID = doc.properties.level; + var style = doc.properties.style; + if (!eventsProgression[userID]) eventsProgression[userID] = []; + eventsProgression[userID].push({ + style: style, + level: levelID, + event: event, + created: created.toISOString() + }) + } + + // print("Sorting per-user events progression data..."); + for (userID in eventsProgression) eventsProgression[userID].sort(function (a,b) {return a.created < b.created ? -1 : 1}); + + // print("Building per-level/style levels completed.."); + var helpClickCounts = {}; + for (userID in eventsProgression) { + // Walk user's history, and tally what preceded each historical entry + var userHistory = eventsProgression[userID]; + var previouslyWatched = {}; + for (var i = 0; i < userHistory.length; i++) { + + // Walk previously watched events, and attribute to correct additionally watched entry + var item = userHistory[i]; + var level = item.level; + var style = item.style; + var event = item.event; + var created = item.created; + + if (event === 'Start help video') { + // Add level/style to previouslyWatched for this user + if (!previouslyWatched[level]) previouslyWatched[level] = {}; + if (!previouslyWatched[level][style]) previouslyWatched[level][style] = true; + } + else if (event === "Problem alert help clicked" || event === "Spell palette help clicked") { + for (previousLevel in previouslyWatched) { + for (previousStyle in previouslyWatched[previousLevel]) { + // For previous level and style, help click followed it + if (!helpClickCounts[previousLevel]) helpClickCounts[previousLevel] = {}; + if (!helpClickCounts[previousLevel][previousStyle]) helpClickCounts[previousLevel][previousStyle] = 0; + helpClickCounts[previousLevel][previousStyle]++; + } + } + } + else { + throw new Error("Unknown event " + event); + } + } + } + + // print("Sorting level completed counts..."); + var helpClicksSorted = []; + for (levelID in helpClickCounts) { + for (style in helpClickCounts[levelID]) { + var data = { + level: levelID, + style: style, + completed: helpClickCounts[levelID][style], + completedPerPlayer: helpClickCounts[levelID][style] / g_videoEventCounts[levelID][style]['Start help video'] + }; + helpClicksSorted.push(data); + } + } + helpClicksSorted.sort(function(a,b) { + if (a.level !== b.level) { + if (a.level < b.level) return -1; + else return 1; + } + return b.completedPerPlayer - a.completedPerPlayer; + }); + + print("Helps clicked after video watched:"); + print("Columns: level, style, click count, clicks per start video"); + for (var i = 0; i < helpClicksSorted.length; i++) { + var item = helpClicksSorted[i]; + if (multiStyleLevels.indexOf(item.level) >= 0) { + print(item.level + "\t" + item.style + (item.style === 'edited' ? "\t\t" : "\t") + item.completed + "\t" + item.completedPerPlayer.toFixed(2)); + } + } +} + +initVideoEventCounts(); +printVideoCompletionRates(); + +printWatchedAnotherVideoRates(); +printLevelCompletionRates(); +printSubConversionTotals(); +printHelpClicksPostHaunted(); diff --git a/scripts/analytics/mongodb/queries/insertPerDayAnalytics.js b/scripts/analytics/mongodb/queries/insertPerDayAnalytics.js new file mode 100644 index 000000000..8961053d7 --- /dev/null +++ b/scripts/analytics/mongodb/queries/insertPerDayAnalytics.js @@ -0,0 +1,420 @@ +// Insert per-day analytics into analytics.perdays collection + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// Completion rates (funnels) are calculated like Mixpanel +// For a given date range, start count is the number of first steps (e.g. started a level) +// Finish count for the same start date is how many unique users finished the remaining steps in the following ~30 days +// https://mixpanel.com/help/questions/articles/how-are-funnels-calculated + +// Drop count: last started or finished level event for a given unique user + +// TODO: Convert this to a node script so it can use proper libraries (e.g. slugify) + +try { + logDB = new Mongo("localhost").getDB("analytics") + var scriptStartTime = new Date(); + var analyticsStringCache = {}; + + // Look at last 30 days, same as Mixpanel + var numDays = 30; + + var startDay = new Date(); + today = startDay.toISOString().substr(0, 10); + startDay.setUTCDate(startDay.getUTCDate() - numDays); + startDay = startDay.toISOString().substr(0, 10); + + var levelCompletionFunnel = ['Started Level', 'Saw Victory']; + var levelHelpEvents = ['Problem alert help clicked', 'Spell palette help clicked', 'Start help video']; + + log("Today is " + today); + log("Start day is " + startDay); + log("Funnel events are " + levelCompletionFunnel); + + log("Getting level completion data..."); + var levelCompletionData = getLevelFunnelData(startDay, levelCompletionFunnel); + log("Inserting aggregated level completion data..."); + for (level in levelCompletionData) { + for (day in levelCompletionData[level]) { + if (today === day) continue; // Never save data for today because it's incomplete + for (event in levelCompletionData[level][day]) { + insertEventCount(event, level, day, levelCompletionData[level][day][event]); + } + } + } + + log("Getting level drop counts..."); + var levelDropCounts = getLevelDropCounts(startDay, levelCompletionFunnel); + log("Inserting level drop counts..."); + for (level in levelDropCounts) { + for (day in levelDropCounts[level]) { + if (today === day) continue; // Never save data for today because it's incomplete + insertEventCount('User Dropped', level, day, levelDropCounts[level][day]); + } + } + + log("Getting level help counts..."); + var levelHelpCounts = getLevelHelpCounts(startDay, levelHelpEvents); + log("Inserting level help counts..."); + for (level in levelHelpCounts) { + for (day in levelHelpCounts[level]) { + if (today === day) continue; // Never save data for today because it's incomplete + for (event in levelHelpCounts[level][day]) { + insertEventCount(event, level, day, levelHelpCounts[level][day][event]); + } + } + } + + log("Getting level subscription counts..."); + var levelSubscriptionCounts = getLevelSubscriptionCounts(startDay); + log("Inserting level subscription counts..."); + for (level in levelSubscriptionCounts) { + for (day in levelSubscriptionCounts[level]) { + if (today === day) continue; // Never save data for today because it's incomplete + for (event in levelSubscriptionCounts[level][day]) { + insertEventCount(event, level, day, levelSubscriptionCounts[level][day][event]); + } + } + } + + log("Script runtime: " + (new Date() - scriptStartTime)); +} +catch(err) { + log("ERROR: " + err); + printjson(err); +} + + +// *** Helper functions *** + +function slugify(text) +// https://gist.github.com/mathewbyrne/1280286 +{ + return text.toString().toLowerCase() + .replace(/\s+/g, '-') // Replace spaces with - + .replace(/[^\w\-]+/g, '') // Remove all non-word chars + .replace(/\-\-+/g, '-') // Replace multiple - with single - + .replace(/^-+/, '') // Trim - from start of text + .replace(/-+$/, ''); // Trim - from end of text +} + +function log(str) { + print(new Date().toISOString() + " " + str); +} + +function objectIdWithTimestamp(timestamp) { + // Convert string date to Date object (otherwise assume timestamp is a date) + if (typeof(timestamp) == 'string') timestamp = new Date(timestamp); + // Convert date object to hex seconds since Unix epoch + var hexSeconds = Math.floor(timestamp/1000).toString(16); + // Create an ObjectId with that hex timestamp + var constructedObjectId = ObjectId(hexSeconds + "0000000000000000"); + return constructedObjectId +} + +function getAnalyticsString(str) { + if (analyticsStringCache[str]) return analyticsStringCache[str]; + + // Find existing string + var doc = db['analytics.strings'].findOne({v: str}); + if (doc) { + analyticsStringCache[str] = doc._id; + return analyticsStringCache[str]; + } + + // Insert string + // http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/#auto-increment-optimistic-loop + doc = {v: str}; + while (true) { + var cursor = db['analytics.strings'].find({}, {_id: 1}).sort({_id: -1}).limit(1); + var seq = cursor.hasNext() ? cursor.next()._id + 1 : 1; + doc._id = seq; + var results = db['analytics.strings'].insert(doc); + if (results.hasWriteError()) { + if ( results.writeError.code == 11000 /* dup key */ ) continue; + else throw new Error("ERROR: Unexpected error inserting data: " + tojson(results)); + } + break; + } + + // Find new string entry + doc = db['analytics.strings'].findOne({v: str}); + if (doc) { + analyticsStringCache[str] = doc._id; + return analyticsStringCache[str]; + } + throw new Error("ERROR: Did not find analytics.strings insert for: " + str); +} + +function getLevelFunnelData(startDay, eventFunnel) { + if (!startDay || !eventFunnel || eventFunnel.length === 0) return {}; + + var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var queryParams = {$and: [{_id: {$gte: startObj}},{"event": {$in: eventFunnel}}]}; + var cursor = logDB['log'].find(queryParams); + + // Map ordering: level, user, event, day + var userDataMap = {}; + while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var day = created.substring(0, 10); + var event = doc.event; + var properties = doc.properties; + var user = doc.user; + var level; + + // TODO: Switch to properties.levelID for 'Saw Victory' + if (event === 'Saw Victory' && properties.level) level = slugify(properties.level); + else if (properties.levelID) level = properties.levelID + else continue + + if (!userDataMap[level]) userDataMap[level] = {}; + if (!userDataMap[level][user]) userDataMap[level][user] = {}; + if (!userDataMap[level][user][event] || userDataMap[level][user][event].localeCompare(day) > 0) { + // if (userDataMap[level][user][event]) log("Found earlier date " + level + " " + event + " " + user + " " + userDataMap[level][user][event] + " " + day); + userDataMap[level][user][event] = day; + } + } + + // Data: level, day, event + var levelFunnelData = {}; + for (level in userDataMap) { + for (user in userDataMap[level]) { + + // Find first event date + var funnelStartDay = null; + for (event in userDataMap[level][user]) { + var day = userDataMap[level][user][event]; + if (!levelFunnelData[level]) levelFunnelData[level] = {}; + if (!levelFunnelData[level][day]) levelFunnelData[level][day] = {}; + if (!levelFunnelData[level][day][event]) levelFunnelData[level][day][event] = 0; + if (eventFunnel[0] === event) { + // First event gets attributed to current date + levelFunnelData[level][day][event]++; + funnelStartDay = day; + break; + } + } + + if (funnelStartDay) { + // Add remaining funnel steps/events to first step's date + for (event in userDataMap[level][user]) { + if (!levelFunnelData[level][funnelStartDay][event]) levelFunnelData[level][funnelStartDay][event] = 0; + if (eventFunnel[0] != event) levelFunnelData[level][funnelStartDay][event]++; + } + // Zero remaining funnel events + for (var i = 1; i < eventFunnel.length; i++) { + var event = eventFunnel[i]; + if (!levelFunnelData[level][funnelStartDay][event]) levelFunnelData[level][funnelStartDay][event] = 0; + } + } + // Else no start event in this date range + } + } + return levelFunnelData; +} + +function getLevelDropCounts(startDay, events) { + // How many unique users did one of these events last? + // Return level/day breakdown + + if (!startDay || !events || events.length === 0) return {}; + + var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var queryParams = {$and: [{_id: {$gte: startObj}},{"event": {$in: events}}]}; + var cursor = logDB['log'].find(queryParams); + + var userProgression = {}; + while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var event = doc.event; + var properties = doc.properties; + var user = doc.user; + var level; + + // TODO: Switch to properties.levelID for 'Saw Victory' + if (event === 'Saw Victory' && properties.level) level = slugify(properties.level); + else if (properties.levelID) level = properties.levelID + else continue + + if (!userProgression[user]) userProgression[user] = []; + userProgression[user].push({ + created: created, + event: event, + level: level + }); + } + + var levelDropCounts = {}; + for (user in userProgression) { + userProgression[user].sort(function (a,b) {return a.created < b.created ? -1 : 1}); + var lastEvent = userProgression[user][userProgression[user].length - 1]; + var level = lastEvent.level; + var day = lastEvent.created.substring(0, 10); + if (!levelDropCounts[level]) levelDropCounts[level] = {}; + if (!levelDropCounts[level][day]) levelDropCounts[level][day] = 0 + levelDropCounts[level][day]++; + } + return levelDropCounts; +} + +function getLevelHelpCounts(startDay, events) { + if (!startDay || !events || events.length === 0) return {}; + + var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var queryParams = {$and: [{_id: {$gte: startObj}},{"event": {$in: events}}]}; + var cursor = logDB['log'].find(queryParams); + + // Map ordering: level, user, event, day + var userDataMap = {}; + while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var day = created.substring(0, 10); + var event = doc.event; + var properties = doc.properties; + var user = doc.user; + var level; + + if (properties.level) level = properties.level; + else if (properties.levelID) level = properties.levelID + else continue + + if (!userDataMap[level]) userDataMap[level] = {}; + if (!userDataMap[level][user]) userDataMap[level][user] = {}; + if (!userDataMap[level][user][event] || userDataMap[level][user][event].localeCompare(day) > 0) { + // if (userDataMap[level][user][event]) log("Found earlier date " + level + " " + event + " " + user + " " + userDataMap[level][user][event] + " " + day); + userDataMap[level][user][event] = day; + } + } + + // Data: level, day, event + var levelEventData = {}; + for (level in userDataMap) { + for (user in userDataMap[level]) { + for (event in userDataMap[level][user]) { + var day = userDataMap[level][user][event]; + if (!levelEventData[level]) levelEventData[level] = {}; + if (!levelEventData[level][day]) levelEventData[level][day] = {}; + if (!levelEventData[level][day][event]) levelEventData[level][day][event] = 0; + levelEventData[level][day][event]++; + } + } + } + return levelEventData; +} + +function getLevelSubscriptionCounts(startDay) { + // Counts subscriptions shown per day, only for events that have levels + // Subscription purchased event counts are attributed to last shown subscription modal event's day and level + if (!startDay) return {}; + + var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var queryParams = {$and: [ + {_id: {$gte: startObj}}, + {$or: [ + {$and: [{'event': 'Show subscription modal'}, {'properties.level': {$exists: true}}]}, + {'event': 'Finished subscription purchase'}] + } + ]}; + var cursor = logDB['log'].find(queryParams); + + // Map ordering: user, event, level, day + // Map ordering: user, event, day + var userDataMap = {}; + while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var day = created.substring(0, 10); + var event = doc.event; + var user = doc.user; + + if (!userDataMap[user]) userDataMap[user] = {}; + + if (event === 'Show subscription modal') { + var level = doc.properties.level; + + // TODO: This is for legacy data. + // TODO: Event tracking updated to use level slug for loading level view on ~1/21/15 + level = slugify(level); + + if (!userDataMap[user][event]) userDataMap[user][event] = {}; + if (!userDataMap[user][event][level] || userDataMap[user][event][level].localeCompare(day) > 0) { + userDataMap[user][event][level] = day; + } + } + else if (event === 'Finished subscription purchase') { + if (!userDataMap[user][event] || userDataMap[user][event].localeCompare(day) > 0) { + userDataMap[user][event] = day; + } + } else { + continue; + } + } + + // Data: level, day, event + var levelFunnelData = {}; + for (user in userDataMap) { + if (userDataMap[user]['Show subscription modal']) { + var lastDay = null; + var lastLevel = null; + for (level in userDataMap[user]['Show subscription modal']) { + var day = userDataMap[user]['Show subscription modal'][level]; + if (!lastDay || lastDay.localeCompare(day) > 0) { + lastDay = day; + lastLevel = level; + } + if (!levelFunnelData[level]) levelFunnelData[level] = {}; + if (!levelFunnelData[level][day]) levelFunnelData[level][day] = {}; + if (!levelFunnelData[level][day][event]) levelFunnelData[level][day]['Show subscription modal'] = 0; + levelFunnelData[level][day]['Show subscription modal']++; + } + if (lastDay && userDataMap[user]['Finished subscription purchase']) { + if (!levelFunnelData[lastLevel][lastDay]['Finished subscription purchase']) { + levelFunnelData[lastLevel][lastDay]['Finished subscription purchase'] = 0; + } + levelFunnelData[lastLevel][lastDay]['Finished subscription purchase']++; + } + } + } + return levelFunnelData; +} + +function insertEventCount(event, level, day, count) { + // analytics.perdays schema in server/analytics/AnalyticsPeryDay.coffee + day = day.replace(/-/g, ''); + + var eventID = getAnalyticsString(event); + var levelID = getAnalyticsString(level); + var filterID = getAnalyticsString('all'); + + var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var queryParams = {$and: [{d: day}, {e: eventID}, {l: levelID}, {f: filterID}]}; + var doc = db['analytics.perdays'].findOne(queryParams); + if (doc && doc.c === count) return; + + if (doc && doc.c !== count) { + // Update existing count, assume new one is more accurate + // log("Updating count in db for " + day + " " + event + " " + level + " " + doc.c + " => " + count); + var results = db['analytics.perdays'].update(queryParams, {$set: {c: count}}); + if (results.nMatched !== 1 && results.nModified !== 1) { + log("ERROR: update event count failed"); + printjson(results); + } + } + else { + var insertDoc = {d: day, e: eventID, l: levelID, f: filterID, c: count}; + var results = db['analytics.perdays'].insert(insertDoc); + if (results.nInserted !== 1) { + log("ERROR: insert event failed"); + printjson(results); + printjson(insertDoc); + } + // else { + // log("Added " + day + " " + event + " " + count + " " + level); + // } + } +} diff --git a/scripts/analytics/mongodb/queries/levelCompletionCounts.js b/scripts/analytics/mongodb/queries/levelCompletionCounts.js new file mode 100644 index 000000000..de7e91ab8 --- /dev/null +++ b/scripts/analytics/mongodb/queries/levelCompletionCounts.js @@ -0,0 +1,153 @@ +// Level completion counts broken down into free and paid buckets + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// TODO: subscriber is someone who is currently subscribed, not necessarily subscribed when they completed a level + +// Excluded for one reason or another +// Some relevant code: https://github.com/codecombat/codecombat/blob/master/app/views/play/CampaignView.coffee#L281-L292 +var excludedLevels = ['deadly-dungeon-rescue', 'kithgard-brawl', 'cavern-survival', 'kithgard-mastery', 'destroying-angel', 'kithgard-apprentice', 'wild-horses', 'lost-viking', 'forest-flower-grove', 'boulder-woods', 'the-trials']; + +var scriptStartTime = new Date(); +var startDay = '2015-05-16'; +var endDay = '2015-06-17'; + +log("Dates: " + startDay + " to " + endDay); + +var subscribers = getSubscribers(); +log("Subscriber count: " + Object.keys(subscribers).length); + +var campaigns = getCampaigns(); +for (var i = 0; i < campaigns.length; i++) { + var campaign = campaigns[i]; + // if (campaign.slug !== 'mountain') continue; + + function printCampaign(title, prop) { + print(title) + print("Total\tFree\tSubscribers"); + for (var j = 0; j < campaign[prop].length; j++) { + var levelSlug = campaign[prop][j]; + if (excludedLevels.indexOf(levelSlug) >= 0) continue; + var data = getCompletionCounts([levelSlug], subscribers); + if (data[levelSlug]) { + var free = data[levelSlug].free.length; + var paid = data[levelSlug].paid.length; + var total = free + paid; + var paidRate = parseInt(paid / total * 100); + print(total + "\t" + free + "\t" + paid + "\t\t" + paidRate + "%\t" + levelSlug); + } + else { + print("0\t0\t0\t\t0%\t" + levelSlug); + } + } + } + + printCampaign(campaign.slug + " (free)", "free"); + printCampaign(campaign.slug + " (paid)", "paid"); + printCampaign(campaign.slug + " (replayable)", "replayable"); + + // break; +} + +log("Script runtime: " + (new Date() - scriptStartTime)); + +function log(str) { + print(new Date().toISOString() + " " + str); +} + +function objectIdWithTimestamp(timestamp) { + // Convert string date to Date object (otherwise assume timestamp is a date) + if (typeof(timestamp) == 'string') timestamp = new Date(timestamp); + // Convert date object to hex seconds since Unix epoch + var hexSeconds = Math.floor(timestamp/1000).toString(16); + // Create an ObjectId with that hex timestamp + var constructedObjectId = ObjectId(hexSeconds + "0000000000000000"); + return constructedObjectId +} + +function getCampaigns() { + var campaigns = []; + var cursor = db.campaigns.find({}, {slug: 1, levels: 1}); + var allFree = 0; + var allpaid = 0; + while (cursor.hasNext()) { + var doc = cursor.next(); + if (doc.slug === 'auditions') continue; + var campaign = {slug: doc.slug, free: [], paid: [], replayable: []}; + for (var levelID in doc.levels) { + if (doc.levels[levelID].replayable) { + campaign.replayable.push(doc.levels[levelID].slug); + } + else if (doc.levels[levelID].requiresSubscription) { + campaign.paid.push(doc.levels[levelID].slug); + } + else { + campaign.free.push(doc.levels[levelID].slug); + } + } + campaigns.push(campaign); + } + return campaigns; +} + +function getCompletionCounts(levelSlugs, subscribers) { + var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var endObj = objectIdWithTimestamp(ISODate(endDay + "T00:00:00.000Z")) + var cursor = db['level.sessions'].find({ + $and: + [ + {"state.complete": true}, + {levelID: {$in: levelSlugs}}, + {_id: {$gte: startObj}}, + {_id: {$lt: endObj}} + ] + }); + + var completionCounts = {}; + while (cursor.hasNext()) { + var myDoc = cursor.next(); + var userID = myDoc.creator; + var levelID = myDoc.levelID; + + if (!completionCounts[levelID]) completionCounts[levelID] = {free: [], paid: []}; + if (subscribers[userID]) { + completionCounts[levelID].paid.push(myDoc._id.valueOf()); + } + else { + completionCounts[levelID].free.push(myDoc._id.valueOf()); + } + } + + return completionCounts; +} + +function getSubscribers() { + var cursor = db['users'].find({ + $and: + [ + { + $or: + [ + {"stripe.sponsorID": {$exists: true}}, + {$and: + [ + {"stripe.subscriptionID": {$exists: true}}, + {"stripe.planID": 'basic'} + ] + } + ] + }, + {permissions: {$ne: ['admin']}}, + {"stripe.free": {$exists: false}}, + {"stripe.coupon": {$exists: false}}, + {"stripe.prepaidCode": {$exists: false}} + ] + }); + + var subscribers = {}; + while (cursor.hasNext()) { + subscribers[cursor.next()._id.valueOf()] = true; + } + return subscribers; +} diff --git a/scripts/analytics/mongodb/queries/levelRates.js b/scripts/analytics/mongodb/queries/levelRates.js new file mode 100644 index 000000000..799d94615 --- /dev/null +++ b/scripts/analytics/mongodb/queries/levelRates.js @@ -0,0 +1,339 @@ +// Print out level completion rates + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// Bucketize start/finish events into days, then bucketize into levels +// Average playtime: level sessions created in timeframe, state.complete = true, then average 'playtime' + +// TODO: Should this be total code problems / levels finished, or total / level session count instead? +// Average code problems: total code problems / levels staretd + +// TODO: Why do a small number of 'Started level' not have properties.levelID set? + +// TODO: Fix addPlaytimeAverages() and addUserCodeProblemCounts() +// TODO: getLevelFunnelData() outputs different data structure now. + +var startTime = new Date(); + +var today = new Date(); +today = today.toISOString().substr(0, 10); +print("Today is " + today); + +// var todayMinus6 = new Date(); +// todayMinus6.setUTCDate(todayMinus6.getUTCDate() - 6); +// var startDate = todayMinus6.toISOString().substr(0, 10) + "T00:00:00.000Z"; +// startDate = "2015-01-23T00:00:00.000Z"; +// print("Start date is " + startDate) +// var endDate = "2015-01-24T00:00:00.000Z"; +// print("End date is " + endDate) + +var levelCompletionFunnel = ['Started Level', 'Saw Victory']; +var dataStartDay = "2015-01-15"; +var startDay = "2015-01-23"; +var endDay = "2015-01-24"; +print(startDay + " to " + endDay); +print("Data start day " + dataStartDay); + +var targetLevels = ['dungeons-of-kithgard']; + +function objectIdWithTimestamp(timestamp) +{ + // Convert string date to Date object (otherwise assume timestamp is a date) + if (typeof(timestamp) == 'string') timestamp = new Date(timestamp); + // Convert date object to hex seconds since Unix epoch + var hexSeconds = Math.floor(timestamp/1000).toString(16); + // Create an ObjectId with that hex timestamp + var constructedObjectId = ObjectId(hexSeconds + "0000000000000000"); + return constructedObjectId +} + +function getLevelFunnelData(startDay, endDay, eventFunnel) { + // Copied from insertPerDayAnalytics.js + if (!startDay || !eventFunnel || eventFunnel.length === 0) return {}; + + // var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); + var startObj = objectIdWithTimestamp(ISODate(dataStartDay + "T00:00:00.000Z")); + var endObj = objectIdWithTimestamp(ISODate(endDay + "T00:00:00.000Z")); + var queryParams = {$and: [{_id: {$gte: startObj}},{_id: {$lt: endObj}},{"event": {$in: eventFunnel}}]}; + // var queryParams = {$and: [{user: ObjectId("539c630f30a67c3b05d98d95")},{_id: {$gte: startObj}},{_id: {$lt: endObj}},{"event": {$in: eventFunnel}}]}; + var cursor = db['analytics.log.events'].find(queryParams); + + // Map ordering: level, user, event, day + var recordCount = 0; + var duplicates = {}; + var levelEventUserDayMap = {}; + var levelUserEventDayMap = {}; + while (cursor.hasNext()) { + recordCount++; + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var day = created.substring(0, 10); + var event = doc.event; + var properties = doc.properties; + var user = doc.user; + var level; + + // TODO: Switch to properties.levelID for 'Saw Victory' + if (event === 'Saw Victory' && properties.level) level = properties.level.toLowerCase().replace(/ /g, '-'); + else if (properties.levelID) level = properties.levelID + else continue + + // if (targetLevels.indexOf(level) < 0) continue; + + // print(day + " " + created); + // print(JSON.stringify(doc, null, 2)); + + if (level.length > longestLevelName) longestLevelName = level.length; + + if (!levelUserEventDayMap[level]) levelUserEventDayMap[level] = {}; + if (!levelUserEventDayMap[level][user]) levelUserEventDayMap[level][user] = {}; + if (levelUserEventDayMap[level][user][event]) { + if (!duplicates[event]) duplicates[event] = 0; + duplicates[event]++; + } + if (!levelUserEventDayMap[level][user][event] || levelUserEventDayMap[level][user][event].localeCompare(day) > 0) { + // if (!levelUserEventDayMap[level][user][event] || day.localeCompare(levelUserEventDayMap[level][user][event]) > 0) { + // day is earlier than levelUserEventDayMap[level][user][event] + levelUserEventDayMap[level][user][event] = day; + } + + if (!levelEventUserDayMap[level]) levelEventUserDayMap[level] = {}; + if (!levelEventUserDayMap[level][event]) levelEventUserDayMap[level][event] = {}; + if (!levelEventUserDayMap[level][event][user] || levelEventUserDayMap[level][event][user].localeCompare(day) > 0) { + levelEventUserDayMap[level][event][user] = day; + } + } + + // print("Records: " + recordCount); + // print("Duplicates"); + // print(JSON.stringify(duplicates, null, 2)); + longestLevelName += 2; + + // Data: level, day, event + var noStartDayUsers = []; + var levelFunnelData = {}; + for (level in levelUserEventDayMap) { + for (user in levelUserEventDayMap[level]) { + + // Find first event date + var funnelStartDay = null; + for (event in levelUserEventDayMap[level][user]) { + var day = levelUserEventDayMap[level][user][event]; + if (day.localeCompare(startDay) < 0) { + // day earlier than startDay + continue; + } + if (!levelFunnelData[level]) levelFunnelData[level] = {}; + if (!levelFunnelData[level][day]) levelFunnelData[level][day] = {}; + if (!levelFunnelData[level][day][event]) levelFunnelData[level][day][event] = 0; + if (eventFunnel[0] === event) { + // First event gets attributed to current date + levelFunnelData[level][day][event]++; + funnelStartDay = day; + break; + } + } + + if (funnelStartDay) { + // Add remaining funnel steps/events to first step's date + for (event in levelUserEventDayMap[level][user]) { + if (!levelFunnelData[level][funnelStartDay][event]) levelFunnelData[level][funnelStartDay][event] = 0; + if (eventFunnel[0] != event) levelFunnelData[level][funnelStartDay][event]++; + } + // Zero remaining funnel events + for (var i = 1; i < eventFunnel.length; i++) { + var event = eventFunnel[i]; + if (!levelFunnelData[level][funnelStartDay][event]) levelFunnelData[level][funnelStartDay][event] = 0; + } + } + else { + // TODO: calc no start days + for (event in levelUserEventDayMap[level][user]) { + var day = levelUserEventDayMap[level][user][event]; + if (day.localeCompare(startDay) < 0) { + // day earlier than startDay + continue; + } + if (eventFunnel[0] != event) { + noStartDayUsers.push(user); + } + } + } + } + } + + // print("No start day count: " + noStartDayUsers.length); + // for (var i = 0; i < noStartDayUsers.length && i < 50; i++) { + // print(noStartDayUsers[i]); + // } + + return levelFunnelData; +} + +function addPlaytimeAverages(startDay, endDay, levelRates) { + print("Getting playtimes..."); + var startObj = objectIdWithTimestamp(ISODate(dataStartDay + "T00:00:00.000Z")); + var endObj = objectIdWithTimestamp(ISODate(endDay + "T00:00:00.000Z")); + // var match = {"$match" : {$and: [{_id: { $gte: startObj}}, {_id: { $lt: endObj}}]}}; + var match = { + "$match" : { + $and: [ + {_id: { $gte: startObj}}, + {_id: { $lt: endObj}}, + {"state.complete": true}, + {"playtime": {$gt: 0}} + ] + }}; + + var proj0 = {"$project": { + "_id" : 0, + "levelID" : 1, + "playtime": 1, + "day": {"$substr" : ["$created", 0, 10]} + }}; + + var group = {"$group" : { + "_id" : { + "day" : "$day", + "level": "$levelID" + }, + "average" : { + "$avg" : "$playtime" + } + }}; + + var cursor = db['level.sessions'].aggregate(match, proj0, group); + + var levelPlaytimeData = {}; + while (cursor.hasNext()) { + var doc = cursor.next(); + var day = doc._id.day; + var level = doc._id.level; + if (!levelPlaytimeData[level]) levelPlaytimeData[level] = {}; + levelPlaytimeData[level][day] = doc.average; + } + + for (levelIndex in levelRates) { + for (dateIndex in levelRates[levelIndex]) { + var level = levelRates[levelIndex][dateIndex].level; + var day = levelRates[levelIndex][dateIndex].day; + if (levelPlaytimeData[level] && levelPlaytimeData[level][day]) { + levelRates[levelIndex][dateIndex].averagePlaytime = levelPlaytimeData[level][day]; + } + } + } +} + +function addUserCodeProblemCounts(startDay, endDay, levelRates) { + print("Getting user code problem counts..."); + var startObj = objectIdWithTimestamp(ISODate(dataStartDay + "T00:00:00.000Z")); + var endObj = objectIdWithTimestamp(ISODate(endDay + "T00:00:00.000Z")); + var match = {"$match" : {$and: [{_id: { $gte: startObj}}, {_id: { $lt: endObj}}]}}; + + var proj0 = {"$project": { + "_id" : 0, + "levelID" : 1, + "day": {"$substr" : ["$created", 0, 10]} + }}; + + var group = {"$group" : { + "_id" : { + "day" : "$day", + "level": "$levelID" + }, + "count" : { + "$sum" : 1 + } + }}; + + var cursor = db['level.sessions'].aggregate(match, proj0, group); + + var levelPlaytimeData = {}; + while (cursor.hasNext()) { + var doc = cursor.next(); + var day = doc._id.day; + var level = doc._id.level; + if (!levelPlaytimeData[level]) levelPlaytimeData[level] = {}; + levelPlaytimeData[level][day] = doc.count; + } + + for (levelIndex in levelRates) { + for (dateIndex in levelRates[levelIndex]) { + var level = levelRates[levelIndex][dateIndex].level; + var day = levelRates[levelIndex][dateIndex].day; + if (levelPlaytimeData[level] && levelPlaytimeData[level][day]) { + levelRates[levelIndex][dateIndex].codeProblems = levelPlaytimeData[level][day]; + } + } + } +} + +var longestLevelName = -1; +var dates = []; + +var levelRates = getLevelFunnelData(startDay, endDay, levelCompletionFunnel); +// addPlaytimeAverages(startDay, endDay, levelRates); +// addUserCodeProblemCounts(startDay, endDay, levelRates); + +// print(JSON.stringify(levelRates, null, 2)) + +// Print out all data +// print("Columns: level, day, started, finished, completion rate, average finish playtime, average code problem count"); +print("Columns: level, day, started, finished, completion rate"); +for (levelKey in levelRates) { + for (dateKey in levelRates[levelKey]) { + // var day = levelRates[levelKey][dateKey].day; + // var level = levelRates[levelKey][dateKey].level; + // var started = levelRates[levelKey][dateKey].started; + // var finished = levelRates[levelKey][dateKey].finished; + var started = levelRates[levelKey][dateKey][levelCompletionFunnel[0]] || 0; + var finished = levelRates[levelKey][dateKey][levelCompletionFunnel[levelCompletionFunnel.length - 1]] || 0; + var completionRate = started > 0 ? finished / started : 0; + // var averagePlaytime = levelRates[levelKey][dateKey].averagePlaytime; + // averagePlaytime = averagePlaytime ? Math.round(averagePlaytime) : 0; + // var averageCodeProblems = levelRates[levelKey][dateKey].codeProblems; + // averageCodeProblems = averageCodeProblems ? (averageCodeProblems / started).toFixed(2) : 0.0; + if ((longestLevelName - levelKey.length) < 0) + throw new Error(longestLevelName + " " + levelKey.length); + var levelSpacer = new Array(longestLevelName - levelKey.length).join(' '); + // print(levelKey + levelSpacer + dateKey + "\t" + started + "\t" + finished + "\t" + (completionRate * 100).toFixed(2) + "% " + averagePlaytime + "s " + averageCodeProblems); + print(levelKey + levelSpacer + dateKey + "\t" + started + "\t" + finished + "\t" + (completionRate * 100).toFixed(2) + "%"); + } +} + +// Print out a nice grid of levels with 7 days of data +// print("Columns: level, completion rate/average playtime/average code problems, completion rate/average playtime/average code problems ..."); +// print(new Array(longestLevelName).join(' ') + dates.join('\t\t')); +// for (levelKey in levelRates) { +// var hasStarted = false; +// for (dateKey in levelRates[levelKey]) { +// if (levelRates[levelKey][dateKey].started > 0) { +// hasStarted = true; +// break; +// } +// } +// if (!hasStarted) continue; +// +// if (levelRates[levelKey].length < 6) continue; +// +// var level = levelRates[levelKey][0].level; +// var levelSpacer = new Array(longestLevelName - level.length).join(' '); +// var msg = level + levelSpacer; +// +// for (dateKey in levelRates[levelKey]) { +// var day = levelRates[levelKey][dateKey].day; +// var started = levelRates[levelKey][dateKey].started; +// var finished = levelRates[levelKey][dateKey].finished; +// var averagePlaytime = levelRates[levelKey][dateKey].averagePlaytime; +// averagePlaytime = averagePlaytime ? Math.round(averagePlaytime) : 0; +// var averageCodeProblems = levelRates[levelKey][dateKey].codeProblems; +// averageCodeProblems = averageCodeProblems ? averageCodeProblems / started : 0.0; +// var completionRate = started > 0 ? finished / started : 0; +// msg += (completionRate * 100).toFixed(2) + "/" + averagePlaytime + "/" + averageCodeProblems.toFixed(2) + "\t"; +// } +// print(msg); +// } + +var endTime = new Date(); +print("Runtime: " + (endTime - startTime)); diff --git a/scripts/analytics/mongodb/queries/mapReduceStartFinishLevelEvents.js b/scripts/analytics/mongodb/queries/mapReduceStartFinishLevelEvents.js new file mode 100644 index 000000000..624fc6cdc --- /dev/null +++ b/scripts/analytics/mongodb/queries/mapReduceStartFinishLevelEvents.js @@ -0,0 +1,87 @@ +// Aggregate start/finish level event counts by day using mapReduce + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// TODO: This basically never returns. Why so slow? +// TODO: For only endangered-burl and dungeons-of-kithgard on 1/7/15 +// TODO: 20s removing uniques in reduce, 7.6s keeping uniques, 9s removing uniques in finalize +// TODO: For all levels on 1/7/15, 136s. Yikes! +// Calling mapReduce... +// { +// "mapTime" : 4924, +// "emitLoop" : 134561, +// "reduceTime" : 128327, +// "mode" : "mixed", +// "total" : 136173 +// } +// { "input" : 98194, "emit" : 98194, "reduce" : 27074, "output" : 202 } + + + +function map() { + var user = this.user; + var event = this.event; + var level; + if (this.properties['level']) { + level = this.properties['level'].toLowerCase().replace(/ /g, '-'); + event = 'finished'; + } + else if (this.properties['levelID']) { + level = this.properties['levelID']; + event = 'started'; + } + else { + return; + } + var created = this.created.toISOString().substring(0, 10); + // Have to wrap array value in an object + // http://stackoverflow.com/questions/8175015/mongodb-mapreduce-reduce-multiple-not-supported-yet + emit({"event": event, "level": level, "created": created}, {users: [user]}); +} + +function reduce(key, values) { + // Combine individual user lists + // values: [{users: [user1, user2]}, {users: [user1, user3]}, ...] + var set = []; + for (var i = 0; i < values.length; i++) set = set.concat(values[i].users); + return {users: set}; +} +function finalize(key, reducedVal) { + // Convert to unique user count + var users = {}; + for (var i = 0; i < reducedVal.users.length; i++) users[reducedVal.users[i]] = true; + return Object.keys(users).length; +} + +print("Calling mapReduce..."); +var output = db['analytics.log.events'].mapReduce(map, reduce, +{ + query: { + $and: [ + {created: {$gte: ISODate("2015-01-07T00:00:00.000Z")}}, + {created: {$lt: ISODate("2015-01-08T00:00:00.000Z")}}, +// {$or: [ {"properties.level": {$exists: true}}, {"properties.levelID": {$exists: true}}]}, + // {$or: [ + // {"properties.level": "Endangered Burl"}, {"properties.levelID": "endangered-burl"}, + // {"properties.level": "Dungeons of Kithgard"}, {"properties.levelID": "dungeons-of-kithgard"} + // ]}, + {$or: [ {"event" : 'Started Level'}, {"event" : 'Saw Victory'}]} + ] + }, + finalize: finalize, + out: { inline: 1 }, + verbose: true +}); + +var results = output.results; +// print(results.length); +printjson(output.timing); +printjson(output.counts); +print("Printing results..."); +for (var i = 0; i < results.length; i++) { +// // if (results[i]["_id"]["level"] === "endangered-burl") +// // print(results[i]["_id"]["created"], results[i]["_id"]["event"], results[i]["value"]); + print(results[i]["_id"]["created"], results[i]["_id"]["event"], results[i]["value"], results[i]["_id"]["level"]); +// printjson(results[i]); +} \ No newline at end of file diff --git a/scripts/analytics/mongodb/queries/parentEmailStats.js b/scripts/analytics/mongodb/queries/parentEmailStats.js new file mode 100644 index 000000000..62afa5173 --- /dev/null +++ b/scripts/analytics/mongodb/queries/parentEmailStats.js @@ -0,0 +1,86 @@ +// Parent emails sent and converted + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// TODO: Count emails to self differently? + +var scriptStartTime = new Date(); +try { + + var emailTypes = ["subscribe modal parent", "share progress modal parent", "share progress modal friend"]; + + // var cursor = db.users.find({$and: [{'stripe': {$exists: true}}, {'emails.oneTimes': {$exists: true}}]}); + var cursor = db.users.find({$and: [{'emails.oneTimes': {$exists: true}}]}); + + var sent = {}; + var converted = {}; + while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var emailOneTimes = doc.emails.oneTimes; + + for (var i = 0; i < doc.emails.oneTimes.length; i++) { + var oneTime = doc.emails.oneTimes[i]; + if (emailTypes.indexOf(oneTime.type) >= 0) { + if (!sent[oneTime.type]) sent[oneTime.type] = {}; + sent[oneTime.type][doc._id.valueOf()] = true; + + var payment = db.payments.findOne({recipient: doc._id}); + if (payment) { + var paymentCreated = payment._id.getTimestamp().toISOString(); + var emailCreated = doc._id.getTimestamp().toISOString(); + // If email created before payment received + if (emailCreated.localeCompare(paymentCreated) < 0) { + if (!converted[oneTime.type]) converted[oneTime.type] = {}; + converted[oneTime.type][doc._id.valueOf()] = true; + } + } + + break; + } + } + } + + // printjson(sent); + // printjson(converted); + + var stats = {}; + + for (var type in sent) { + if (!stats[type]) stats[type] = {}; + stats[type].sent = Object.keys(sent[type]).length; + } + for (var type in converted) { + stats[type].converted = Object.keys(converted[type]).length; + stats[type].rate = (stats[type].converted / stats[type].sent * 100).toFixed(2); + } + + log("Sent\tConverted\tRate\tType"); + for (var type in stats) { + log(stats[type].sent + "\t" + stats[type].converted + "\t" + stats[type].rate + "\t" + type); + } +} +catch(err) { + log("ERROR: " + err); + printjson(err); +} +finally { + log("Script runtime: " + (new Date() - scriptStartTime)); +} + +// *** Helper functions *** + +function log(str) { + print(new Date().toISOString() + " " + str); +} + +function objectIdWithTimestamp(timestamp) { + // Convert string date to Date object (otherwise assume timestamp is a date) + if (typeof(timestamp) == 'string') timestamp = new Date(timestamp); + // Convert date object to hex seconds since Unix epoch + var hexSeconds = Math.floor(timestamp/1000).toString(16); + // Create an ObjectId with that hex timestamp + var constructedObjectId = ObjectId(hexSeconds + "0000000000000000"); + return constructedObjectId +} diff --git a/scripts/analytics/mongodb/queries/purchaseEventHistories.js b/scripts/analytics/mongodb/queries/purchaseEventHistories.js new file mode 100644 index 000000000..7e7e18047 --- /dev/null +++ b/scripts/analytics/mongodb/queries/purchaseEventHistories.js @@ -0,0 +1,121 @@ +// Analyze sub purchase decisions via user event histories + +// Usage: +// mongo <address>:<port>/analytics <script file> -u <username> -p <password> + +// NOTES +// Starting from beginning wasn't as interesting at looking at 20 events or so before and after purchase +// + +// TODO: Group events: clicked level, inventory play, started level => started level X +// TODO: What event-specific data should we grab? +// TODO: How do we compare users? + +var scriptStartTime = new Date(); + +try { + var analyticsStringCache = {}; + var analyticsStringIDCache = {}; + + var numSubscribers = 30; + var beforeCount = 20; + var afterCount = 10; + + var excludedEvents = ['Inventory Play']; + + var subscribers = getSubscribers(numSubscribers); + log("Retrieved subscribers: " + subscribers.length); + + var histories = getHistories(subscribers, excludedEvents, beforeCount, afterCount); + + for(var user in histories) { + print(user + " " + histories[user].length); + for (var i = 0; i < histories[user].length; i++) { + print(histories[user][i].created + " " + i + " " + histories[user][i].event + " " + histories[user][i].level); + } + } + +} +catch(err) { + log("ERROR: " + err); + printjson(err); +} +finally { + log("Script runtime: " + (new Date() - scriptStartTime)); +} + +// *** Helper functions *** + +function log(str) { + print(new Date().toISOString() + " " + str); +} + +function getSubscribers(count) { + if (!count || count < 1) return []; + + var queryParams = {event: 'Finished subscription purchase'}; + var cursor = db['log'].find(queryParams).sort({_id: -1}).limit(count); + + var subscribers = []; + while (cursor.hasNext()) { + var doc = cursor.next(); + // if (doc.user !== '5491a42c037fb13f0741dac5') continue; + subscribers.push({created: doc._id, userID: doc.user}); + } + return subscribers; +} + +function getHistories(subscribers, excludedEvents, beforeCount, afterCount) { + if (!subscribers) return {}; + + var userEventsMap = {}; + for (var i = 0; i < subscribers.length; i++) { + var subscriber = subscribers[i]; + var user = subscriber.userID.valueOf(); + userEventsMap[user] = []; + + var queryParams = {$and: [{_id: {$lte: subscriber.created}},{user: subscriber.userID}]}; + var cursor = db['log'].find(queryParams).sort({_id: -1}).limit(beforeCount); + + while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var event = doc.event; + + if (excludedEvents.indexOf(event) >= 0) continue; + + var properties = doc.properties; + var level = ''; + if (properties) { + level = properties.levelID || properties.level || ''; + if (properties.label) { + level += ' ' + properties.label; + } + } + userEventsMap[user].push({event: event, created: created, level: level}); + } + userEventsMap[user].sort(function (a,b) {return a.created < b.created ? -1 : 1}); + + queryParams = {$and: [{_id: {$gt: subscriber.created}},{user: subscriber.userID}]}; + cursor = db['log'].find(queryParams).sort({_id: 1}).limit(afterCount); + + while (cursor.hasNext()) { + var doc = cursor.next(); + var created = doc._id.getTimestamp().toISOString(); + var event = doc.event; + + if (excludedEvents.indexOf(event) >= 0) continue; + var properties = doc.properties; + var level = ''; + if (properties) { + level = properties.levelID || properties.level || ''; + if (properties.label) { + level += ' ' + properties.label; + } + } + userEventsMap[user].push({event: event, created: created, level: level}); + } + } + + return userEventsMap; +} diff --git a/scripts/analytics/mongodb/queries/spokenLanguageUsage.js b/scripts/analytics/mongodb/queries/spokenLanguageUsage.js new file mode 100644 index 000000000..2fd85a836 --- /dev/null +++ b/scripts/analytics/mongodb/queries/spokenLanguageUsage.js @@ -0,0 +1,36 @@ +// Print out spoken language usage based on signed-in user data + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +var total = 0; +var languages = {}; +//var startDate = new ISODate("2014-12-01T00:00:00.000Z"); +var cursor = db['users'].aggregate( +[ + { $match : { + //$and: [{codeLanguage: {$exists: true}}, {created : { $gte: startDate}}] + anonymous: false + } + }, + { + $group : { + _id: "$preferredLanguage", + total: {$sum: 1} + } + }, + { $sort : { total : -1} } +]); + +while (cursor.hasNext()) { + var myDoc = cursor.next(); + total += myDoc.total; + var lang = myDoc._id || 'en-US'; + if (!languages[myDoc._id]) + languages[myDoc._id] = 0 + languages[myDoc._id] += myDoc.total +} +print("Total registered users with spoken languages", total); +for (key in languages) { + print(languages[key] + "\t" + (languages[key] / total * 100).toFixed(2) + "%\t" + key); +} diff --git a/scripts/analytics/mongodb/queries/teacherSurveyCounts.js b/scripts/analytics/mongodb/queries/teacherSurveyCounts.js new file mode 100644 index 000000000..2bae4a745 --- /dev/null +++ b/scripts/analytics/mongodb/queries/teacherSurveyCounts.js @@ -0,0 +1,25 @@ + // Print out teacher survey counts by day + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +var surveyDayMap = {}; +var cursor = db['trial.requests'].find({type: 'subscription'}); +while (cursor.hasNext()) { + var doc = cursor.next(); + var date = doc._id.getTimestamp(); + var day = date.toISOString().substring(0, 10); + if (!surveyDayMap[day]) surveyDayMap[day] = 0; + surveyDayMap[day]++; +} + +var surveysSorted = []; +for (var day in surveyDayMap) { + surveysSorted.push({day: day, count: surveyDayMap[day]}); +} +surveysSorted.sort(function(a, b) {return b.day.localeCompare(a.day);}); +print("Number of teacher surveys per day:") +for (var i = 0; i < surveysSorted.length; i++) { + var stars = new Array(surveysSorted[i].count + 1).join('*'); + print(surveysSorted[i].day + "\t" + surveysSorted[i].count + "\t" + stars); +} diff --git a/scripts/analytics/parseMixpanelPayments.py b/scripts/analytics/parseMixpanelPayments.py new file mode 100644 index 000000000..c8f6a3742 --- /dev/null +++ b/scripts/analytics/parseMixpanelPayments.py @@ -0,0 +1,188 @@ +# Parse subscription conversion rates via Mixpanel raw export API + +import sys +from datetime import tzinfo, timedelta, datetime +from mixpanel import Mixpanel + +try: + import json +except ImportError: + import simplejson as json + +# NOTE: mixpanel dates are by day and inclusive +# E.g. '2014-12-08' is any date that day, up to 2014-12-09 12am + +def printPriceConversionRates(api_key, api_secret, startDate, endDate): + # dateCreated is in UTC + + # Dec 8th subscribe copy A/B test added + + # 599 - 1st HoC 599 sale started: Dec 9 6:23am PST + # 999 - 1st HoC 599 sale ended: Dec 10 4:34pm PST + # 1499 - sub price test starts: Dec 10 5:00pm PST + # Only for dateCreated >= 5pm PST + # 399 - 2nd HoC 399 sale started: Dec 11 7:21pm PST + # 999 - 2nd HoC sale ended: Dec 13 9:30am PST + # UTC is +8 hrs + + api = Mixpanel( + api_key = api_key, + api_secret = api_secret + ) + + print 'Requesting Mixpanel data' + # data = api.request(['events'], { + # 'event' : ['Finished subscription purchase',], + # 'unit' : 'hour', + # 'interval' : 24, + # 'type': 'general' + # }) + # data = api.request(['funnels', 'list'], {}) + data = api.request(['export'], { + 'event' : ['Show subscription modal', 'Finished subscription purchase',], + # 'event' : ['Finished subscription purchase',], + # 'event' : ['Show subscription modal',], + 'from_date' : startDate, + 'to_date' : endDate + }) + + prices = { + '399': { + 'start': datetime(2014, 12, 12, 3, 21), + 'end': datetime(2014, 12, 13, 17, 30) + }, + '599': { + 'start': datetime(2014, 12, 9, 14, 23), + 'end': datetime(2014, 12, 11, 0, 34) + }, + '999': { + 'start': datetime(2014, 9, 1), + 'end': datetime(2014, 12, 9, 14, 23), + 'start2': datetime(2014, 12, 11, 0, 34), + 'end2': datetime(2014, 12, 12, 3, 21), + 'start3': datetime(2014, 12, 13, 17, 30) + }, + '1499': { + 'start': datetime(2014, 12, 11, 1), + 'end': datetime(2014, 12, 12, 3, 21) + } + } + + # id vs distinct_id ? + def addEvent(price, event, id): + if not event in price: + price[event] = {} + price[event][id] = True + elif not id in price[event]: + price[event][id] = True + + def getPriceStr(eventDateStr, userDateStr): + priceStr = '999' + eventCreated = datetime.utcfromtimestamp(int(eventDateStr)) + # Put events in buckets based on creation times + if eventCreated >= prices['599']['start'] and eventCreated < prices['599']['end']: + priceStr = '599' + elif eventCreated >= prices['999']['start2'] and eventCreated < prices['999']['end2']: + # In 999/1499 zone + # Create a datetime from: 2014-12-11T12:37:59 + userCreated = datetime(int(userDateStr[0:4]), int(userDateStr[5:7]), int(userDateStr[8:10]), int(userDateStr[11:13]), int(userDateStr[14:16]), int(userDateStr[17:19])) + if userCreated >= prices['1499']['start']: + priceStr = '1499' + elif eventCreated >= prices['399']['start'] and eventCreated < prices['399']['end']: + priceStr = '399' + return priceStr + + lines = data.split('\n') + print "Received %d entries" % len(lines) + for line in lines: + try: + if len(line) is 0: continue + event = json.loads(line) + properties = event['properties'] + if not event['event'] in ['Show subscription modal', 'Finished subscription purchase']: + print 'Unexpected event ' + event['event'] + break + # print 'Processing', event['event'], properties['time'], properties['dateCreated'] + if 'dateCreated' in properties and 'time' in properties and 'distinct_id' in properties: + # NOTE: mixpanel conversions don't account for refunds + # NOTE: So we have an extra 1499 hit for mattcc4021@gmaIl.com / 5488ee8a600bc8b206771ba3 + if properties['distinct_id'] == '5488ee8a600bc8b206771ba3': + # ch_155tz8KaReE7xLUdQpsa9aqe, cus_5GQqAosNHuRQCQ + # print 'Skipping mattcc4021@gmaIl.com / 5488ee8a600bc8b206771ba3' + # print event['event'], properties['distinct_id'] + continue + # if properties['distinct_id'] == '54790dacfd5b8f550584aaf3': + # print 'Found a time example 54790dacfd5b8f550584aaf3' + # print properties['time'], datetime.utcfromtimestamp(int(properties['time'])) + priceStr = getPriceStr(properties['time'], properties['dateCreated']) + # if priceStr == '1499' and event['event'] == 'Finished subscription purchase': + # print 'Found a 1499 payment', properties['distinct_id'] + addEvent(prices[priceStr], event['event'], properties['distinct_id']) + except: + print "Unexpected error:", sys.exc_info()[0] + print line + break + + print 'Price, converted, shown, conversion rate, value per user' + for key, item in prices.iteritems(): + # 'Show subscription modal', 'Finished subscription purchase' + converted = shown = 0 + if 'Finished subscription purchase' in item: + converted = len(item['Finished subscription purchase'].keys()) + if 'Show subscription modal' in item: + shown = len(item['Show subscription modal'].keys()) + if shown > 0: + print key, converted, shown, "%.4f%%" % (float(converted) / shown * 100), "%.4f cents" % (float(converted) / shown * int(key)) + else: + print key, converted, shown + + +def getShownSubModal(api_key, api_secret, startDate, endDate): + # print 'Requesting Mixpanel data' + api = Mixpanel( + api_key = api_key, + api_secret = api_secret + ) + data = api.request(['export'], { + 'event' : ['Show subscription modal',], + 'from_date' : startDate, + 'to_date' : endDate + }) + + uniques = set() + # biggestDate = 0 + + lines = data.split('\n') + # print "Received %d entries" % len(lines) + for line in lines: + try: + if len(line) is 0: continue + event = json.loads(line) + properties = event['properties'] + if not event['event'] in ['Show subscription modal']: + print 'Unexpected event ' + event['event'] + break + # print 'Processing', event['event'], properties['time'], properties['dateCreated'] + if 'distinct_id' in properties and not properties['distinct_id'] in uniques: + uniques.add(properties['distinct_id']) + # if int(properties['time']) > biggestDate: + # biggestDate = int(properties['time']) + except: + print "Unexpected error:", sys.exc_info()[0] + print line + break + # print 'Biggest date:', datetime.utcfromtimestamp(int(properties['time'])) + return len(uniques) + +if __name__ == '__main__': + if not len(sys.argv) is 3: + print "Script format: <script> <api_key> <api_secret>" + else: + api_key = sys.argv[1] + api_secret = sys.argv[2] + # HoC + printPriceConversionRates(api_key, api_secret, '2014-12-08', '2014-12-19') + + # Use these to feed numbers into Stripe parsing script, since Stripe knows better about conversions than Mixpanel + print 'Pre-HoC shown', getShownSubModal(api_key, api_secret, '2014-12-06', '2014-12-07') + print 'Post-HoC shown', getShownSubModal(api_key, api_secret, '2014-12-20', '2015-01-04') diff --git a/scripts/analytics/parseStripePayments.py b/scripts/analytics/parseStripePayments.py new file mode 100644 index 000000000..c548d0ae4 --- /dev/null +++ b/scripts/analytics/parseStripePayments.py @@ -0,0 +1,247 @@ +# Parse Stripe payment info via exported payments.csv files + +import sys +from datetime import tzinfo, timedelta, datetime + +# TODO: use stripe_customers.csv to match payments to our db data + +# Stripe file format +# id,Description,Created (UTC),Amount,Amount Refunded,Currency,Converted Amount,Converted Amount Refunded,Fee,Tax,Converted Currency,Mode,Status,Statement Description,Customer ID,Customer Description,Customer Email,Captured,Card Last4,Card Brand,Card Funding,Card Exp Month,Card Exp Year,Card Name,Card Address Line1,Card Address Line2,Card Address City,Card Address State,Card Address Country,Card Address Zip,Card Issue Country,Card Fingerprint,Card CVC Status,Card AVS Zip Status,Card AVS Line1 Status,Disputed Amount,Dispute Status,Dispute Reason,Dispute Date (UTC),Dispute Evidence Due (UTC),Invoice ID,productID (metadata),userID (metadata),gems (metadata),timestamp (metadata) + +def getGemCounts(paymentsFile): + gems = {} + with open(paymentsFile) as f: + first = True + for line in f: + if first: + first = False + else: + data = line.split(',') + amount = int(float(data[3]) * 100) + status = data[12] + statementDescription = data[13] + if status == 'Paid' and not statementDescription == 'Sub': + if not amount in gems: + gems[amount] = 1 + else: + gems[amount] += 1 + return gems + +def getSubCounts(paymentsFile): + subs = {} + with open(paymentsFile) as f: + first = True + for line in f: + if first: + first = False + else: + data = line.split(',') + # created = data[2] + amount = int(float(data[3]) * 100) + # amountRefunded = int(float(data[4]) * 100) + # mode = data[11] + status = data[12] + statementDescription = data[13] + + # Look for status = 'Paid', and statementDescription = 'Sub' + # print "{0}\t{1}\t{2}\t{3}\t{4}\t{5}".format(created, amount, amountRefunded, mode, status, statementDescription) + + if status == 'Paid' and statementDescription == 'Sub': + if not amount in subs: + subs[amount] = 1 + else: + subs[amount] += 1 + return subs + +def getHoCPriceConversionRates(paymentsFile): + # Show counts from Mixpanel + prices = { + '399': { + # 'start': datetime(2014, 12, 12, 3, 21), + # 'end': datetime(2014, 12, 13, 17, 30), + 'Show subscription modal': 31157, + 'Finished subscription purchase': 0 + }, + '599': { + # 'start': datetime(2014, 12, 9, 14, 23), + # 'end': datetime(2014, 12, 11, 0, 34), + 'Show subscription modal': 31044, + 'Finished subscription purchase': 0 + }, + '999': { + # 'start': datetime(2014, 9, 1), + # 'end': datetime(2014, 12, 9, 14, 23), + # 'start2': datetime(2014, 12, 11, 0, 34), + # 'end2': datetime(2014, 12, 12, 3, 21), + # 'start3': datetime(2014, 12, 13, 17, 30), + 'Show subscription modal': 86883, + 'Finished subscription purchase': 0 + }, + '1499': { + # 'start': datetime(2014, 12, 11, 1), + # 'end': datetime(2014, 12, 12, 3, 21), + 'Show subscription modal': 19519, + 'Finished subscription purchase': 0 + } + } + + # TODO: may be one 1499 sale + priceTest = { + 'ch_158LyeKaReE7xLUdnt0m9pjb': True, + 'ch_158OPLKaReE7xLUdcqYQ5qst': True, + 'ch_158jkBKaReE7xLUd305I3WBy': True + } + + # Find 'Finished subscription purchase' event from Stripe data + startDate = datetime(2014, 12, 8) + endDate = datetime(2014, 12, 20) + print startDate, 'to', endDate + with open(paymentsFile) as f: + first = True + for line in f: + if first: + first = False + else: + data = line.split(',') + paymentID = data[0] + created = data[2] # 2014-12-14 06:01 + + createdDate = datetime(int(created[0:4]), int(created[5:7]), int(created[8:10]), int(created[11:13]), int(created[14:16])) + if createdDate < startDate or createdDate >= endDate: + continue + + if paymentID in priceTest: + amount = 1499 + else: + amount = int(float(data[3]) * 100) + amountStr = str(amount) + # amountRefunded = int(float(data[4]) * 100) + # mode = data[11] + status = data[12] + statementDescription = data[13] + + # Look for status = 'Paid', and statementDescription = 'Sub' + # print "{0}\t{1}\t{2}\t{3}\t{4}\t{5}".format(created, amount, amountRefunded, mode, status, statementDescription) + + if status == 'Paid' and statementDescription == 'Sub': + prices[amountStr]['Finished subscription purchase'] += 1 + + # Calculate conversion rates + for key, item in prices.iteritems(): + item['Conversion Rate'] = float(item['Finished subscription purchase']) / item['Show subscription modal'] + item['Value Per User'] = float(item['Finished subscription purchase']) / item['Show subscription modal'] * int(key) + + return prices + +def getPreHoCPriceConversionRates(paymentsFile): + # Pre-HoC but after full stop paywall in forest + + # Show count from Mixpanel + prices = { + '999': { + 'Show subscription modal': 3447, + 'Finished subscription purchase': 0 + } + } + + # Find 'Finished subscription purchase' event from Stripe data + startDate = datetime(2014, 12, 6) + endDate = datetime(2014, 12, 8) + print startDate, 'to', endDate + with open(paymentsFile) as f: + first = True + for line in f: + if first: + first = False + else: + data = line.split(',') + paymentID = data[0] + created = data[2] # 2014-12-14 06:01 + createdDate = datetime(int(created[0:4]), int(created[5:7]), int(created[8:10]), int(created[11:13]), int(created[14:16])) + if createdDate < startDate or createdDate >= endDate: + continue + amount = int(float(data[3]) * 100) + amountStr = str(amount) + status = data[12] + statementDescription = data[13] + if status == 'Paid' and statementDescription == 'Sub': + prices[amountStr]['Finished subscription purchase'] += 1 + + # Calculate conversion rates + for key, item in prices.iteritems(): + item['Conversion Rate'] = float(item['Finished subscription purchase']) / item['Show subscription modal'] + item['Value Per User'] = float(item['Finished subscription purchase']) / item['Show subscription modal'] * int(key) + + return prices + +def getPostHoCPriceConversionRates(paymentsFile): + # Pre-HoC but after full stop paywall in forest + + # Show count from Mixpanel + prices = { + '999': { + 'Show subscription modal': 13339, + 'Finished subscription purchase': 0 + } + } + + # Find 'Finished subscription purchase' event from Stripe data + startDate = datetime(2014, 12, 20) + endDate = datetime(2015, 1, 4) + print startDate, 'to', endDate + with open(paymentsFile) as f: + first = True + for line in f: + if first: + first = False + else: + data = line.split(',') + paymentID = data[0] + created = data[2] # 2014-12-14 06:01 + createdDate = datetime(int(created[0:4]), int(created[5:7]), int(created[8:10]), int(created[11:13]), int(created[14:16])) + if createdDate < startDate or createdDate >= endDate: + continue + amount = int(float(data[3]) * 100) + amountStr = str(amount) + status = data[12] + statementDescription = data[13] + if status == 'Paid' and statementDescription == 'Sub': + prices[amountStr]['Finished subscription purchase'] += 1 + + # Calculate conversion rates + for key, item in prices.iteritems(): + item['Conversion Rate'] = float(item['Finished subscription purchase']) / item['Show subscription modal'] + item['Value Per User'] = float(item['Finished subscription purchase']) / item['Show subscription modal'] * int(key) + + return prices + +if __name__ == '__main__': + paymentsFile = 'stripe_payments.csv' + if len(sys.argv) is 2: + paymentsFile = sys.argv[1] + print 'Processing', paymentsFile + + print 'Subs' + print getSubCounts(paymentsFile) + + print 'Gems' + print getGemCounts(paymentsFile) + + print 'Pre-HoC Conversion Rates' + priceConversionRates = getPreHoCPriceConversionRates(paymentsFile) + print 'Price, converted, shown, conversion rate, value per user' + for key, item in priceConversionRates.iteritems(): + print key, item['Finished subscription purchase'], item['Show subscription modal'], "%.4f%%" % (item['Conversion Rate'] * 100), "%.4f cents" % (item['Conversion Rate'] * int(key)) + + print 'HoC Conversion Rates' + priceConversionRates = getHoCPriceConversionRates(paymentsFile) + print 'Price, converted, shown, conversion rate, value per user' + for key, item in priceConversionRates.iteritems(): + print key, item['Finished subscription purchase'], item['Show subscription modal'], "%.4f%%" % (item['Conversion Rate'] * 100), "%.4f cents" % (item['Conversion Rate'] * int(key)) + + print 'Post-HoC Conversion Rates' + priceConversionRates = getPostHoCPriceConversionRates(paymentsFile) + print 'Price, converted, shown, conversion rate, value per user' + for key, item in priceConversionRates.iteritems(): + print key, item['Finished subscription purchase'], item['Show subscription modal'], "%.4f%%" % (item['Conversion Rate'] * 100), "%.4f cents" % (item['Conversion Rate'] * int(key)) + diff --git a/scripts/analytics/stripeSubscribers.js b/scripts/analytics/stripeSubscribers.js new file mode 100644 index 000000000..f3f3c1c43 --- /dev/null +++ b/scripts/analytics/stripeSubscribers.js @@ -0,0 +1,99 @@ +// Find latest Stripe basic subscribers who haven't cancelled + +// TODO: doesn't handle 11+ sponsored subs for a given customer + +if (process.argv.length !== 3) { + console.log("Usage: node <script> <API key>"); + process.exit(); +} + +var apiKey = process.argv[2]; +var stripe = require("stripe")(apiKey); + +var yesterday = new Date(); +yesterday.setUTCDate(yesterday.getUTCDate() - 1); + +var earliestDate = new Date("2015-03-01T00:00:00.000Z"); + +var subs = {}; + +getSubscriptions(null, function () { + console.log('Recurring sub counts:'); + for (var created in subs) { + for (var amount in subs[created]) { + if (parseInt(amount) > 0) { + console.log(created, amount, subs[created][amount]['recurring']) + } + } + } +}); + + +function getSubscriptions(starting_after, done) +{ + var options = {limit: 100}; + if (starting_after) options.starting_after = starting_after; + + stripe.customers.list(options, function(err, customers) { + for (var i = 0; i < customers.data.length; i++) { + var customer = customers.data[i]; + + if (customer.subscriptions && customer.subscriptions.data.length > 0) { + var basic = false; + for (var j = 0;j < customer.subscriptions.data.length; j++) { + var subscription = customer.subscriptions.data[j]; + if (subscription.plan.id === 'basic') { + + var created = new Date(subscription.start * 1000); + if (created < earliestDate) continue; + created = created.toISOString().substring(0, 10); + if (!subs[created]) subs[created] = {}; + + var amount = subscription.plan.amount; + if (subscription.discount && subscription.discount.coupon) { + if (subscription.discount.coupon.percent_off) { + amount = amount * (100 - subscription.discount.coupon.percent_off) / 100; + } + else if (subscription.discount.coupon.amount_off) { + amount -= subscription.discount.coupon.amount_off; + } + } + else if (customer.discount && customer.discount.coupon) { + if (customer.discount.coupon.percent_off) { + amount = amount * (100 - customer.discount.coupon.percent_off) / 100; + } + else if (customer.discount.coupon.amount_off) { + amount -= customer.discount.coupon.amount_off; + } + } + + if (!subs[created][amount]) subs[created][amount] = {}; + + if (subscription.cancel_at_period_end === true) { + if (!subs[created][amount]['cancelled']) subs[created][amount]['cancelled'] = 0; + subs[created][amount]['cancelled']++; + } + else { + if (!subs[created][amount]['recurring']) subs[created][amount]['recurring'] = {}; + + if (customer.alipay_accounts && customer.alipay_accounts.total_count) { + if (!subs[created][amount]['recurring']['alipay']) subs[created][amount]['recurring']['alipay'] = 0; + subs[created][amount]['recurring']['alipay']++; + } + else { + if (!subs[created][amount]['recurring']['card']) subs[created][amount]['recurring']['card'] = 0; + subs[created][amount]['recurring']['card']++; + } + } + } + } + } + } + if (customers.has_more) { + getSubscriptions(customers.data[customers.data.length - 1].id, done); + } + else { + done(); + } + }); +} diff --git a/scripts/analytics/subscriptionStats.js b/scripts/analytics/subscriptionStats.js new file mode 100644 index 000000000..3ffe9267b --- /dev/null +++ b/scripts/analytics/subscriptionStats.js @@ -0,0 +1,73 @@ +// To use: set the range you want below, make sure your environment has the stripe key, then run: +// node scripts/analytics/subscriptionStats.js + +require('coffee-script'); +require('coffee-script/register'); +_ = require('lodash'); + +config = require('../../server_config'); +if(config.stripe.secretKey.indexOf('sk_test_')==0) { + throw new Error('You should not run this on the test data... Get your environment in gear.'); +} + +stripe = require('stripe')(config.stripe.secretKey); + +var range = { + gt: ''+(new Date('2015-03-01').getTime()/1000), + lt: ''+(new Date('2015-04-01').getTime()/1000) +}; + +begin = function(starting_after) { + var query = {date: range, limit: 100}; + if(starting_after) { + query.starting_after = starting_after; + } + stripe.invoices.list(query, onInvoicesReceived); +} + +customersPaid = []; + +onInvoicesReceived = function(err, invoices) { + for(var i in invoices.data) { + var invoice = invoices.data[i]; + if(!invoice.paid) { continue; } + if(!invoice.total) { continue; } // not paying anything! + //console.log(invoice); + customersPaid.push(invoice.customer); + } + if(invoices.has_more) { + console.log('Loaded', customersPaid.length, 'invoices.') + begin(invoices.data[i].id); + } + else { + console.log('--- Actual active total customers:', _.unique(customersPaid).length); + loadNewCustomers(); + } +}; + +loadNewCustomers = function(starting_after) { + query = {created: range, limit: 100}; + if(starting_after) { + query.starting_after = starting_after; + } + stripe.customers.list(query, onCustomersReceived); +}; + +newCustomersPaid = []; + +onCustomersReceived = function(err, customers) { + for(var i in customers.data) { + var customer = customers.data[i]; + if(customersPaid.indexOf(customer.id) == -1) { continue; } + newCustomersPaid.push(customer.id); + } + if(customers.has_more) { + console.log('Loaded', newCustomersPaid.length, 'new customers.'); + loadNewCustomers(customers.data[i].id); + } + else { + console.log('--- Actual new customers:', newCustomersPaid.length); + } +}; + +begin(); \ No newline at end of file diff --git a/scripts/copy-i18n-tags.coffee b/scripts/copy-i18n-tags.coffee index c325aea88..9955562c0 100644 --- a/scripts/copy-i18n-tags.coffee +++ b/scripts/copy-i18n-tags.coffee @@ -7,7 +7,8 @@ commentsMap = {} categorySplitPattern = /^[\s\n]*(?=[^:\n]+:\s*$)/gm categoryCapturePattern = /^([^:\n]+):\s*\n/ -commentPattern = /^[\s\n]*([^:\n]+):\s*"[^#\n"]+"\s*#(.*)$/gm +commentPattern = /^[\s\n]*([^:\n]+):\s*"[^#\n"]+"\s*#(.*)$/gm # " for Emacs close quote +changePattern = /\s?\s?(#\s)?\{change\}/g splitByCategories = enSource.split(categorySplitPattern) @@ -25,6 +26,7 @@ for section in splitByCategories dir = fs.readdirSync 'app/locale' for file in dir when not (file in ['locale.coffee', 'en.coffee']) + fileSource = fs.readFileSync 'app/locale/' + file, encoding='utf8' contents = require('../app/locale/' + file) categories = contents.translation lines = ["module.exports = nativeDescription: \"#{contents.nativeDescription}\", englishDescription: \"#{contents.englishDescription}\", translation:"] @@ -38,12 +40,23 @@ for file in dir when not (file in ['locale.coffee', 'en.coffee']) for enTag, enString of enTags tagMissing = not cat[enTag]? tag = (cat[enTag] ?= enString) - tag = tag.replace /"/g, '\\"' + tag = tag.replace /"/g, '\\"' # ' for Emacs close quote comment = "" if commentsMap[enCat]? and commentsMap[enCat][enTag]? comment = " \##{commentsMap[enCat][enTag]}" + if fileSource.search(new RegExp("# #{enTag}")) >= 0 # current tag is commented + comment = comment.replace changePattern, "" + else + escapedTag = tag.replace /[-\/\\^$*+?.()|[\]{}]/g, "\\$&" + if fileSource.search(new RegExp("^ #{enTag}: \"#{escapedTag}\".*\{change\}.*", 'm')) >= 0 and comment.search(/.*\{change\}/) < 0 + comment = " \#" + comment if comment is "" + comment = comment + " {change}" + lines.push "#{if tagMissing then '#' else ''} #{enTag}: \"#{tag}\"#{comment}" newContents = lines.join('\n') + '\n' fs.writeFileSync 'app/locale/' + file, newContents + +enSource = enSource.replace changePattern, "" +fs.writeFileSync 'app/locale/en.coffee', enSource diff --git a/scripts/deleteUnpaidGemPayments.js b/scripts/deleteUnpaidGemPayments.js new file mode 100644 index 000000000..21c145176 --- /dev/null +++ b/scripts/deleteUnpaidGemPayments.js @@ -0,0 +1,114 @@ +// Delete unpaid gem payments based on Stripe charges +// Assumes prod env variables + +var actuallyDeletePayments = false; + +var async = require('async'); +var apiKey = process.env.COCO_STRIPE_SECRET_KEY +var stripe = require("stripe")(apiKey); + +var MongoClient = require('mongodb').MongoClient; +var mongoUrl = "mongodb://"; +mongoUrl += process.env.COCO_MONGO_USERNAME + ":"; +mongoUrl += process.env.COCO_MONGO_PASSWORD + "@"; +mongoUrl += process.env.COCO_MONGO_HOST + ":" + process.env.COCO_MONGO_PORT + "/"; +mongoUrl += process.env.MONGO_DATABASE_NAME; +console.log(mongoUrl); + +function deleteUnpaidPayments(paymentsToDelete, done) { + if (!actuallyDeletePayments) { + console.log('Would have deleted unpaid payments: ' + paymentsToDelete.length); + console.log(paymentsToDelete); + return done(); + } + + console.log('Deleting unpaid payments... ' + paymentsToDelete.length); + console.log(paymentsToDelete); + MongoClient.connect(mongoUrl, function (err, db) { + if (err) { + console.log(err); + return done(); + } + db.collection('payments').remove({_id: {$in: paymentsToDelete}}, function(err, result) { + if (err) { + console.log(err); + db.close() + return done([]); + } + console.log(result.result); + return done(); + }); + }); +} + +function findUnpaidPayments(payments) { + console.log('Finding unpaid payments... ' + payments.length); + var chargePaymentMap = {}; + var tasks = []; + for (var i = 0; i < payments.length; i++) { + chargePaymentMap[payments[i].stripe.chargeID] = payments[i]; + tasks.push(makeCheckCharge(payments[i].stripe.chargeID)); + } + async.series(tasks, function(err, failedCharges) { + var paymentsToDelete = []; + if (err) { + console.log(err); + } + else { + for (var i = 0; i < failedCharges.length; i++) { + var charge = failedCharges[i]; + if (!charge) continue; + var payment = chargePaymentMap[charge.id]; + if (charge.id === payment.stripe.chargeID && parseInt(charge.metadata.timestamp) === payment.stripe.timestamp && + charge.metadata.userID == payment.purchaser) { + paymentsToDelete.push(payment._id); + } + else { + console.log("ERROR! " + charge.id); + console.log(charge.metadata); + console.log(chargePaymentMap[charge.id]); + console.log(charge.metadata.userID === payment.purchaser); + console.log(charge.metadata.userID == payment.purchaser); + break; + } + } + } + + deleteUnpaidPayments(paymentsToDelete, function() { + console.log('Done.'); + process.exit(); + }); + }); +} + +function makeCheckCharge(chargeID) { + return function(done) { + stripe.charges.retrieve(chargeID, function(err, charge) { + // Ignoring non-null err here because there are invalid (test) charges in our production database + if (!err && charge.status === 'failed') { + return done(null, charge); + } + return done(); + }); + }; +} + +function getPayments(done) { + console.log('Fetching payments...'); + MongoClient.connect(mongoUrl, function (err, db) { + if (err) { + console.log(err); + return done([]); + } + db.collection('payments').find({productID: {$exists: true}}).toArray(function(err, docs) { + if (err) { + console.log(err); + db.close() + return done([]); + } + return done(docs); + }); + }); +} + +getPayments(findUnpaidPayments); diff --git a/scripts/devSetup/bootstrap.sh b/scripts/devSetup/bootstrap.sh index 46099635c..6317d861c 100644 --- a/scripts/devSetup/bootstrap.sh +++ b/scripts/devSetup/bootstrap.sh @@ -31,7 +31,7 @@ function basicDependenciesErrorHandling { exit 1 ;; "git") - echo "Please install Git.(If you're running mac, this is included in the XCode command line tools." + echo "Please install Git (if you're running mac, this is included in the XCode command line tools)." esac } diff --git a/scripts/devSetup/configuration.py b/scripts/devSetup/configuration.py index 2eb0be39c..80ab85198 100644 --- a/scripts/devSetup/configuration.py +++ b/scripts/devSetup/configuration.py @@ -14,6 +14,3 @@ class Configuration(object): @property def mem_width(self): return self.system.virtual_memory_address_width - - - diff --git a/scripts/devSetup/dependency.py b/scripts/devSetup/dependency.py index 2cc726e86..6ef107dbf 100644 --- a/scripts/devSetup/dependency.py +++ b/scripts/devSetup/dependency.py @@ -10,4 +10,3 @@ class Dependency(object): raise NotImplementedError def install_dependencies(self): raise NotImplementedError - diff --git a/scripts/devSetup/directoryController.py b/scripts/devSetup/directoryController.py index 5087db888..bea7cbeea 100644 --- a/scripts/devSetup/directoryController.py +++ b/scripts/devSetup/directoryController.py @@ -50,6 +50,3 @@ class DirectoryController(object): shutil.rmtree(self.bin_directory + os.sep + "mongo",ignore_errors=True) def remove_tmp_directory(self): shutil.rmtree(self.tmp_directory) - - - diff --git a/scripts/devSetup/downloader.py b/scripts/devSetup/downloader.py index 477af3b44..2fe266489 100644 --- a/scripts/devSetup/downloader.py +++ b/scripts/devSetup/downloader.py @@ -38,4 +38,3 @@ class Downloader: else: stringToDisplay = '\r File size unknown. Read {0} bytes.'.format(amount_of_data_downloaded_so_far) print(stringToDisplay,end=' ') - diff --git a/scripts/devSetup/factories.py b/scripts/devSetup/factories.py index 1eab847bb..f76099b6f 100644 --- a/scripts/devSetup/factories.py +++ b/scripts/devSetup/factories.py @@ -71,7 +71,7 @@ class SetupFactory(object): print("NOTE: brunch may need to be run as sudo if it doesn't work (ulimit needs to be set higher than default)") print("") print("Before can play any levels you must update the database. See the Setup section here:") - print("https://github.com/codecombat/codecombat/wiki/Developer-environment#setup") + print("https://github.com/codecombat/codecombat/wiki/Dev-Setup:-Linux#installing-the-database") print("") print("Go to http://localhost:3000 to see your local CodeCombat in action!") def cleanup(self): @@ -80,9 +80,92 @@ class SetupFactory(object): class MacSetup(SetupFactory): def setup(self): super(self.__class__, self).setup() + class WinSetup(SetupFactory): def setup(self): super(self.__class__, self).setup() + class LinuxSetup(SetupFactory): def setup(self): + self.distroSetup() super(self.__class__, self).setup() + + def detectDistro(self): + distro_checks = { + "arch": "/etc/arch-release", + "ubuntu": "/etc/lsb-release" + } + for distro, path in distro_checks.items(): + if os.path.exists(path): + return(distro) + + def distroSetup(self): + distro = self.detectDistro() + if distro == "ubuntu": + print("Ubuntu installation detected. Would you like to install \n" + "NodeJS and MongoDB via apt-get? [y/N]") + if raw_input().lower() in ["y", "yes"]: + print("Adding repositories for MongoDB and NodeJS...") + try: + subprocess.check_call(["apt-key", "adv", + "--keyserver", + "hkp://keyserver.ubuntu.com:80", + "--recv", "7F0CEB10"]) + subprocess.check_call(["add-apt-repository", + "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen"]) + subprocess.check_call("curl -sL " + "https://deb.nodesource.com/setup" + " | bash", shell=True) + subprocess.check_call(["apt-get", "update"]) + except subprocess.CalledProcessError as err: + print("Adding repositories failed. Retry, Install without" + "adding \nrepositories, Skip apt-get installation, " + "or Abort? [r/i/s/A]") + answer = raw_input().lower() + if answer in ["r", "retry"]: + return(self.distroSetup()) + elif answer in ["i", "install"]: + pass + elif answer in ["s", "skip"]: + return() + else: + exit(1) + else: + try: + print("Repositories added successfully. Installing NodeJS and MongoDB.") + subprocess.check_call(["apt-get", "install", + "nodejs", "mongodb-org", + "build-essential", "-y"]) + except subprocess.CalledProcessError as err: + print("Installation via apt-get failed. \nContinue " + "with manual installation, or Abort? [c/A]") + if raw_input().lower() in ["c", "continue"]: + return() + else: + exit(1) + else: + print("NodeJS and MongoDB installed successfully. " + "Starting MongoDB.") + #try: + #subprocess.check_call(["service", "mongod", "start"]) + #except subprocess.CalledProcessError as err: + #print("Mongo failed to start. Aborting.") + #exit(1) + if distro == "arch": + print("Arch Linux detected. Would you like to install \n" + "NodeJS and MongoDB via pacman? [y/N]") + if raw_input().lower() in ["y", "yes"]: + try: + subprocess.check_call(["pacman", "-S", + "nodejs", "mongodb", + "--noconfirm"]) + except subprocess.CalledProcessError as err: + print("Installation failed. Retry, Continue, or " + "Abort? [r/c/A]") + answer = raw_input().lower() + if answer in ["r", "retry"]: + return(self.distroSetup()) + elif answer in ["c", "continue"]: + return() + else: + exit(1) diff --git a/scripts/devSetup/mongo.py b/scripts/devSetup/mongo.py index eb57ea452..a1bd36b78 100644 --- a/scripts/devSetup/mongo.py +++ b/scripts/devSetup/mongo.py @@ -86,10 +86,10 @@ class LinuxMongoDBDownloader(MongoDBDownloader): @property def download_url(self): if self.dependency.config.mem_width == 64: - return u"http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-latest.tgz" + return u"http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.2.tgz" else: warnings.warn(u"MongoDB *really* doesn't run well on 32 bit systems. You have been warned.") - return u"http://fastdl.mongodb.org/linux/mongodb-linux-i686-latest.tgz" + return u"http://fastdl.mongodb.org/linux/mongodb-linux-i686-3.0.2.tgz" class WindowsMongoDBDownloader(MongoDBDownloader): @property @@ -97,13 +97,11 @@ class WindowsMongoDBDownloader(MongoDBDownloader): #TODO: Implement Windows Vista detection warnings.warn(u"If you have a version of Windows older than 7, MongoDB may not function properly!") if self.dependency.config.mem_width == 64: - return u"http://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-latest.zip" + return u"http://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-3.0.2.zip" else: - return u"http://fastdl.mongodb.org/win32/mongodb-win32-i386-latest.zip" + return u"http://fastdl.mongodb.org/win32/mongodb-win32-i386-3.0.2.zip" class MacMongoDBDownloader(MongoDBDownloader): @property def download_url(self): - return u"http://fastdl.mongodb.org/osx/mongodb-osx-x86_64-latest.tgz" - - + return u"http://fastdl.mongodb.org/osx/mongodb-osx-x86_64-3.0.2.tgz" diff --git a/scripts/devSetup/node.py b/scripts/devSetup/node.py index 8fb1265d8..9c8fc574f 100644 --- a/scripts/devSetup/node.py +++ b/scripts/devSetup/node.py @@ -60,7 +60,7 @@ class Node(Dependency): shutil.copytree(self.findUnzippedNodePath(),install_directory) wants_to_upgrade = True if self.check_if_executable_installed(u"npm"): - warning_string = u"A previous version of npm has been found. \nYou may experience problems if you have a version of npm that's too old.Would you like to upgrade?(y/n) " + warning_string = u"A previous version of npm has been found. \nYou may experience problems if you have a version of npm that's too old. Would you like to upgrade?(y/n) " from distutils.util import strtobool print(warning_string) #for bash script, you have to somehow redirect stdin to raw_input() @@ -137,9 +137,9 @@ class LinuxNodeDownloader(NodeDownloader): @property def download_url(self): if self.dependency.config.mem_width == 64: - return u"http://nodejs.org/dist/v0.10.24/node-v0.10.24-linux-x64.tar.gz" + return u"http://nodejs.org/dist/v0.10.35/node-v0.10.35-linux-x64.tar.gz" else: - return u"http://nodejs.org/dist/v0.10.24/node-v0.10.24-linux-x86.tar.gz" + return u"http://nodejs.org/dist/v0.10.35/node-v0.10.35-linux-x86.tar.gz" class WindowsNodeDownloader(NodeDownloader): @property @@ -147,16 +147,14 @@ class WindowsNodeDownloader(NodeDownloader): raise NotImplementedError(u"Needs MSI to be executed to install npm") #"http://nodejs.org/dist/v0.10.24/x64/node-v0.10.24-x64.msi" if self.dependency.config.mem_width == 64: - return u"http://nodejs.org/dist/v0.10.24/x64/node.exe" + return u"http://nodejs.org/dist/v0.10.35/x64/node.exe" else: - return u"http://nodejs.org/dist/v0.10.24/node.exe" + return u"http://nodejs.org/dist/v0.10.35/node.exe" class MacNodeDownloader(NodeDownloader): @property def download_url(self): if self.dependency.config.mem_width == 64: - return u"http://nodejs.org/dist/v0.10.24/node-v0.10.24-darwin-x64.tar.gz" + return u"http://nodejs.org/dist/v0.10.35/node-v0.10.35-darwin-x64.tar.gz" else: - return u"http://nodejs.org/dist/v0.10.24/node-v0.10.24-darwin-x86.tar.gz" - - + return u"http://nodejs.org/dist/v0.10.35/node-v0.10.35-darwin-x86.tar.gz" diff --git a/scripts/devSetup/repositoryInstaller.py b/scripts/devSetup/repositoryInstaller.py index 3e854b876..d612eb4b4 100644 --- a/scripts/devSetup/repositoryInstaller.py +++ b/scripts/devSetup/repositoryInstaller.py @@ -4,6 +4,7 @@ import configuration import errors import subprocess import os +import sys from which import which #git clone https://github.com/nwinter/codecombat.git coco class RepositoryInstaller(): @@ -19,7 +20,7 @@ class RepositoryInstaller(): #http://stackoverflow.com/questions/9329243/xcode-4-4-and-later-install-command-line-tools if not self.checkIfCurlExecutableExists(): if self.config.system.operating_system == "linux": - raise errors.CoCoError("Curl is missing. Please install it(try 'sudo apt-get install curl')\nIf you are not using Ubuntu then please see your Linux Distribution's documentation for help installing curl.") + raise errors.CoCoError("Curl is missing. Please install it (try 'sudo apt-get install curl')\nIf you are not using Ubuntu then please see your Linux Distribution's documentation for help installing curl.") elif self.config.system.operating_system == "mac": raise errors.CoCoError("Curl is missing. Please install the Xcode command line tools.") raise errors.CoCoError(u"Git is missing. Please install git.") @@ -64,7 +65,14 @@ class RepositoryInstaller(): #TODO: "Replace npm with more robust package #npm_location = self.config.directory.bin_directory + os.sep + "node" + os.sep + "bin" + os.sep + "npm" npm_location = u"npm" - return_code = subprocess.call([npm_location,u"install"],cwd=self.config.directory.root_dir + os.sep + u"coco") + if sys.version_info[0] == 2: + py_cmd = "python" + else: + py_cmd = subprocess.check_output(['which', 'python2']) + return_code = subprocess.call([npm_location, u"install", + "--python=" + py_cmd], + cwd=self.config.directory.root_dir + + os.sep + u"coco") if return_code: raise errors.CoCoError(u"Failed to install node packages") else: diff --git a/scripts/devSetup/setup.py b/scripts/devSetup/setup.py index 5c4165bdc..2d2c2ed97 100644 --- a/scripts/devSetup/setup.py +++ b/scripts/devSetup/setup.py @@ -40,9 +40,7 @@ def check_if_root(): raise errors.CoCoError(u"You need to be root. Run as sudo.") if __name__ == u"__main__": - print("Code Combat Development Environment Setup Script") + print("CodeCombat Development Environment Setup Script") check_if_root() setup = factories.constructSetup() setup.setup() - - diff --git a/scripts/mongodb/addExternalSubs.js b/scripts/mongodb/addExternalSubs.js new file mode 100644 index 000000000..5dd4bcfe7 --- /dev/null +++ b/scripts/mongodb/addExternalSubs.js @@ -0,0 +1,70 @@ +// Add externally purchased subscriptions + +// Usage: +// Edit hard coded data below. +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +// Skips duplicate payments, but this might be incorrect depending on the scenario + +// TODO: output emails not found + +var emails = ['pam@fred.com', 'Bob@fred.com']; +var purchaserID = '54ed0ac0ca7f1c421c025b3d'; +var endDate = '2015-06-01'; +var gems = 10500; +var amount = 1750; +var service = 'external'; + +emails = emails.map(function(e) { return e.toLowerCase();}); + +log("Input Data"); +log("service\t" + service); +log("purchaserID\t" + purchaserID); +log("end date\t" + endDate); +log("gems\t" + gems); +log("amount\t" + amount); +log("emails"); +log(emails); + +// 1. Set free = endDate, updated purchased.gems + +db.users.update( + {emailLower: {$in: emails}, "stripe.free": {$ne: endDate}}, + { + $set: { + "stripe.free": endDate + }, + $inc: { + "purchased.gems": gems + } + }, + { + multi: true + } +); + +// 2. create Payment objects + +var cursor = db.users.find({emailLower: {$in: emails}}); +while (cursor.hasNext()) { + var doc = cursor.next(); + + var criteria = { + purchaser: ObjectId(purchaserID), + recipient: doc._id, + service: service, + gems: gems, + amount: amount + } + if (db.payments.findOne(criteria)) { + log("Already have a payment for " + doc.email); + } + else { + db.payments.insert(criteria); + log("Added payment for " + doc.email); + } +} + +function log(str) { + print(new Date().toISOString() + " " + str); +} diff --git a/scripts/mongodb/createBulkPrepaids.js b/scripts/mongodb/createBulkPrepaids.js new file mode 100644 index 000000000..fa9610440 --- /dev/null +++ b/scripts/mongodb/createBulkPrepaids.js @@ -0,0 +1,60 @@ +// Bulk create prepaid codes + email message + +// Usage: +// mongo <address>:<port>/<database> <script file> -u <username> -p <password> + +var num = 10; +var message = "Thanks for filling out the form. You can follow this link to enable your free subscription for teachers. If you have any questions or comments, please let me know."; +var urlPrefix = "https://codecombat.com/account/subscription?_ppc="; +var creatorID = "52f94443fcb334581466a992"; + +for (var i = 0; i < num; i++) { + createPrepaid(); +} + +function createPrepaid() +{ + generateNewCode(function(code) { + if (!code) { + print("ERROR: no code"); + return; + } + criteria = { + creator: creatorID, + type: 'subscription', + status: 'active', + code: code, + properties: { + couponID: 'free' + }, + __v: 0 + }; + db.prepaids.insert(criteria); + + print(message + " " + urlPrefix + code); + }); +} + +function generateNewCode(done) +{ + function tryCode() { + code = createCode(8); + criteria = {code: code}; + if (db.prepaids.findOne(criteria)) { + return tryCode(); + } + return done(code); + } + tryCode(); +} + +function createCode(length) +{ + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for( var i=0; i < length; i++ ) + text += possible.charAt(Math.floor(Math.random() * possible.length)); + + return text; +} diff --git a/scripts/mongodb/queries/patches.js b/scripts/mongodb/queries/patches.js index 771aadd21..2582f5f10 100644 --- a/scripts/mongodb/queries/patches.js +++ b/scripts/mongodb/queries/patches.js @@ -7,12 +7,18 @@ for(var i in patches) { if(patch.target.collection === 'level') collection = db.levels; if(patch.target.collection === 'level_component') collection = db.level.components; if(patch.target.collection === 'level_system') collection = db.level.systems; - if(patch.target.collection === 'thang_type') collection = db.level.thang.types; + if(patch.target.collection === 'thang_type') collection = db.thang.types; + if(patch.target.collection === 'achievement') collection = db.achievements; + if(patch.target.collection === 'article') collection = db.articles; + if(patch.target.collection === 'campaign') collection = db.campaigns; + if(patch.target.collection === 'poll') collection = db.polls; if(collection === null) { print('could not find collection', patch.target.collection); continue; } var target = collection.findOne({original:patch.target.original, name:{$exists:true}}); + if(target === null) + target = collection.findOne({_id:ObjectId(patch.target.original), name:{$exists:true}}); var creator = db.users.findOne({_id:patch.creator}); if(target === null) { print('No target for patch from', patch.target.collection); @@ -22,6 +28,12 @@ for(var i in patches) { print(target.name, 'made by unknown person...'); continue; } - - print(target.name, 'made by', creator.name); - } \ No newline at end of file + + var editor = patch.target.collection + '/'; + if(editor === 'level_component/' || editor === 'level_system/') + editor = 'level/items?' + patch.target.collection + '='; + if(editor === 'thang_type/') + editor = 'thang/'; + var url = 'http://localhost:3000/editor/' + editor + target.slug; + print(url + '\t' + creator.name + '\t' + target.name); +} diff --git a/scripts/mongodb/queries/playtime-breakdown.coffee b/scripts/mongodb/queries/playtime-breakdown.coffee new file mode 100644 index 000000000..9dc65ac13 --- /dev/null +++ b/scripts/mongodb/queries/playtime-breakdown.coffee @@ -0,0 +1,164 @@ +# Life's too short to write these things in JS, so install cofmon: +# npm install -g cofmon +# Then you can paste CoffeeScript into it. + +nDays = 3 +dayOffset = 0.2 +now = new Date() +startDate = new Date(now - 86400 * 1000 * (nDays + dayOffset)) +endDate = new Date(now - 86400 * 1000 * dayOffset) + +users = db.users.find({dateCreated: {$gt: startDate, $lt: endDate}}, {_id: 1, name: 1, testGroupNumber: 1, email: true}).toArray() +goodUsers = [] +for user in users when user.email + totalPlaytime = 0 + sessions = db.level.sessions.find({creator: '' + user._id}, {playtime: 1, levelID: 1}).toArray() + firstSessions = [] + for session in sessions when session.playtime + totalPlaytime += session.playtime + if totalPlaytime > 60 * 60 + break + firstSessions.push session + if totalPlaytime < 60 * 60 + continue + goodUsers.push {user: user, playtime: totalPlaytime, sessions: firstSessions} + +levelUserCounts = {} +for user in goodUsers + for session in user.sessions + levelUserCounts[session.levelID] ?= 0 + levelUserCounts[session.levelID]++ + +print "Found #{goodUsers.length} users who played more than an hour out of #{users.length}." +print "Levels by number of users completing:" +levelUserCounts + + +""" +Found 194 users who played more than an hour out of 93952. +rs0:PRIMARY> levelUserCounts; +{ + "dungeons-of-kithgard" : 190, + "gems-in-the-deep" : 184, + "shadow-guard" : 186, + "forgetful-gemsmith" : 189, + "kounter-kithwise" : 80, + "true-names" : 186, + "favorable-odds" : 76, + "the-raised-sword" : 181, + "haunted-kithmaze" : 181, + "descending-further" : 70, + "the-second-kithmaze" : 171, + "dread-door" : 172, + "known-enemy" : 170, + "master-of-names" : 160, + "lowly-kithmen" : 138, + "closing-the-distance" : 137, + "tactical-strike" : 48, + "the-final-kithmaze" : 108, + "the-gauntlet" : 43, + "kithgard-gates" : 96, + "defense-of-plainswood" : 88, + "winding-trail" : 75, + "endangered-burl" : 51, + "village-guard" : 40, + "thornbush-farm" : 33, + "back-to-back" : 27, + "ogre-encampment" : 22, + "woodland-cleaver" : 18, + "shield-rush" : 10, + "peasant-protection" : 8, + "munchkin-swarm" : 10, + "munchkin-harvest" : 4, + "swift-dagger" : 1, + "shrapnel" : 1, + "arcane-ally" : 1, + "touch-of-death" : 1 + "bonemender" : 1, + "coinucopia" : 6, + "copper-meadows" : 3, + "drop-the-flag" : 3, + "deadly-pursuit" : 2, + "rich-forager" : 1, + "multiplayer-treasure-grove" : 1, + + "rescue-mission" : 2, + "dungeon-arena-tutorial" : 3, + "dungeon-arena" : 2, + "undefined" : 2, + "grab-the-mushroom" : 2, + "gold-rush" : 1, + "criss-cross" : 1, +} +""" + +# With usernames, 3 days instead of 1: +""" +Found 532 users who played more than an hour out of 277828. +> +> print("Levels by number of users completing:"); +Levels by number of users completing: +> +> levelUserCounts; +{ + "dungeons-of-kithgard" : 524, + "gems-in-the-deep" : 513, + "shadow-guard" : 515, + "kounter-kithwise" : 229, + "forgetful-gemsmith" : 520, + "true-names" : 516, + "favorable-odds" : 216, + "the-raised-sword" : 504, + "haunted-kithmaze" : 494, + "the-second-kithmaze" : 472, + "dread-door" : 475, + "known-enemy" : 463, + "master-of-names" : 440, + "lowly-kithmen" : 403, + "closing-the-distance" : 393, + "tactical-strike" : 139, + "the-final-kithmaze" : 321, + "the-gauntlet" : 113, + "kithgard-gates" : 253, + "defense-of-plainswood" : 236, + "descending-further" : 200, + "winding-trail" : 196, + "endangered-burl" : 133, + "village-guard" : 118, + "thornbush-farm" : 89, + "back-to-back" : 77, + "ogre-encampment" : 66, + "woodland-cleaver" : 56, + "shield-rush" : 32, + "peasant-protection" : 30, + "munchkin-swarm" : 28, + "munchkin-harvest" : 9, + "swift-dagger" : 3, + "shrapnel" : 1, + "arcane-ally" : 10, + "bonemender" : 4, + "coinucopia" : 21, + "copper-meadows" : 17, + "drop-the-flag" : 11, + "deadly-pursuit" : 13, + "rich-forager" : 6, + + "undefined" : 8, + "dungeon-arena-tutorial" : 8, + "dungeon-arena" : 8, + "grab-the-mushroom" : 6, + "gold-rush" : 6, + "criss-cross" : 4, + "rescue-mission" : 3, + "touch-of-death" : 2, + "taunt-the-guards" : 1, + "taunt" : 1, + "sky-span" : 1, + "greed" : 1, + "dungeon-battle" : 1, + "drink-me" : 1, + "cowardly-taunt" : 1, + "bubble-sort-bootcamp-battle" : 1, + "break-the-prison" : 1 +} +""" diff --git a/scripts/mongodb/unsubmit-ladder.js b/scripts/mongodb/unsubmit-ladder.js new file mode 100644 index 000000000..db7933ff2 --- /dev/null +++ b/scripts/mongodb/unsubmit-ladder.js @@ -0,0 +1,22 @@ +var levelID = 'zero-sum'; +var sessions = db.level.sessions.find({levelID: levelID, submitted: true}).toArray(); +for (var i = 0; i < sessions.length; ++i) { + var session = sessions[i]; + if (session.creatorName == 'The AI') continue; + if (!session.submittedCode) continue; + print("Unsubmitting " + session.creatorName + " " + session.team); + session.submitted = false; + db.level.sessions.save(session); +} + +// // Resubmit +// var levelID = 'zero-sum'; +// var sessions = db.level.sessions.find({levelID: levelID, submitted: false}).toArray(); +// for (var i = 0; i < sessions.length; ++i) { +// var session = sessions[i]; +// if (session.creatorName == 'The AI') continue; +// if (!session.submittedCode) continue; +// print("Resubmitting " + session.creatorName + " " + session.team); +// session.submitted = true; // false; +// db.level.sessions.save(session); +// } diff --git a/scripts/recalculateAchievements.coffee b/scripts/recalculateAchievements.coffee index 2ec5e02c0..b18495f7d 100644 --- a/scripts/recalculateAchievements.coffee +++ b/scripts/recalculateAchievements.coffee @@ -8,6 +8,7 @@ do (setupLodash = this) -> GLOBAL._ = require 'lodash' _.str = require 'underscore.string' _.mixin _.str.exports() + GLOBAL.tv4 = require('tv4').tv4 database.connect() diff --git a/scripts/recalculatePayments.coffee b/scripts/recalculatePayments.coffee new file mode 100644 index 000000000..5bc28e2de --- /dev/null +++ b/scripts/recalculatePayments.coffee @@ -0,0 +1,55 @@ +database = require '../server/commons/database' +mongoose = require 'mongoose' +log = require 'winston' +async = require 'async' + +### SET UP ### +do (setupLodash = this) -> + GLOBAL._ = require 'lodash' + _.str = require 'underscore.string' + _.mixin _.str.exports() + GLOBAL.tv4 = require('tv4').tv4 + +database.connect() + +User = require '../server/users/User' +Payment = require '../server/payments/Payment' +PaymentHandler = require '../server/payments/payment_handler' + +t0 = new Date().getTime() +total = 100000 +#testUsers = ['livelily+test31@gmail.com', 'livelily+test37@gmail.com'] +if testUsers? + userQuery = emailLower: {$in: testUsers} +else + userQuery = $or: [ + {stripe: {$exists: true}} + {'purchased.gems': {$gt: 0}} + ] +User.count userQuery, (err, count) -> total = count + +onFinished = -> + t1 = new Date().getTime() + runningTime = ((t1-t0)/1000/60/60).toFixed(2) + console.log "we finished in #{runningTime} hours" + process.exit() + +userStream = User.find(userQuery).sort('_id').stream() +streamFinished = false +usersTotal = 0 +usersFinished = 0 +numberRunning = 0 +doneWithUser = -> + ++usersFinished + numberRunning -= 1 + userStream.resume() + onFinished?() if streamFinished and usersFinished is usersTotal + +userStream.on 'error', (err) -> log.error err +userStream.on 'close', -> streamFinished = true +userStream.on 'data', (user) -> + ++usersTotal + numberRunning += 1 + userStream.pause() if numberRunning > 20 + user._id = user.get('_id') + PaymentHandler.recalculateGemsFor user, doneWithUser, true diff --git a/scripts/recalculateStatistics.coffee b/scripts/recalculateStatistics.coffee index 7c54478af..a23551085 100644 --- a/scripts/recalculateStatistics.coffee +++ b/scripts/recalculateStatistics.coffee @@ -28,18 +28,18 @@ whenAllFinished = -> async.parallel [ # Misc (c) -> report UserHandler.recalculateStats, 'gamesCompleted', c - + # Edits (c) -> report UserHandler.recalculateStats, 'articleEdits', c (c) -> report UserHandler.recalculateStats, 'levelEdits', c (c) -> report UserHandler.recalculateStats, 'levelComponentEdits', c (c) -> report UserHandler.recalculateStats, 'levelSystemEdits', c (c) -> report UserHandler.recalculateStats, 'thangTypeEdits', c - + # Patches (c) -> report UserHandler.recalculateStats, 'patchesContributed', c (c) -> report UserHandler.recalculateStats, 'patchesSubmitted', c - + # Patches in memory (c) -> report UserHandler.recalculateStats, 'totalTranslationPatches', c (c) -> report UserHandler.recalculateStats, 'totalMiscPatches', c diff --git a/scripts/recreateEarnedAchievements.coffee b/scripts/recreateEarnedAchievements.coffee new file mode 100644 index 000000000..fba5d1eaf --- /dev/null +++ b/scripts/recreateEarnedAchievements.coffee @@ -0,0 +1,85 @@ +database = require '../server/commons/database' +mongoose = require 'mongoose' +log = require 'winston' +async = require 'async' + +### SET UP ### +do (setupLodash = this) -> + GLOBAL._ = require 'lodash' + _.str = require 'underscore.string' + _.mixin _.str.exports() + GLOBAL.tv4 = require('tv4').tv4 + +database.connect() + +LocalMongo = require '../app/lib/LocalMongo' +User = require '../server/users/User' +EarnedAchievement = require '../server/achievements/EarnedAchievement' +Achievement = require '../server/achievements/Achievement' +Achievement.loadAchievements (achievementCategories) -> + # Really, it's just the 'users' category, since we don't keep all the LevelSession achievements in memory, rather letting the clients make those. + userAchievements = achievementCategories.users + console.log 'There are', userAchievements.length, 'user achievements.' + + t0 = new Date().getTime() + total = 100000 + #testUsers = ['livelily+test31@gmail.com', 'livelily+test37@gmail.com'] + if testUsers? + userQuery = emailLower: {$in: testUsers} + else + userQuery = anonymous: false + User.count userQuery, (err, count) -> total = count + + onFinished = -> + t1 = new Date().getTime() + runningTime = ((t1-t0)/1000/60/60).toFixed(2) + console.log "we finished in #{runningTime} hours" + process.exit() + + # Fetch every single user. This tends to get big so do it in a streaming fashion. + userStream = User.find(userQuery).sort('_id').stream() + streamFinished = false + usersTotal = 0 + usersFinished = 0 + totalAchievementsExisting = 0 + totalAchievementsCreated = 0 + numberRunning = 0 + doneWithUser = -> + ++usersFinished + numberRunning -= 1 + userStream.resume() + onFinished?() if streamFinished and usersFinished is usersTotal + + userStream.on 'error', (err) -> log.error err + userStream.on 'close', -> streamFinished = true + userStream.on 'data', (user) -> + ++usersTotal + numberRunning += 1 + userStream.pause() if numberRunning > 20 + userID = user.get('_id').toHexString() + userObject = user.toObject() + + # Fetch all of a user's earned achievements + EarnedAchievement.find {user: userID}, (err, alreadyEarnedAchievements) -> + log.error err if err + + achievementsExisting = 0 + achievementsCreated = 0 + for achievement in userAchievements + #console.log "Testing", achievement.get('name'), achievement.get('_id') if testUsers? + shouldBeAchieved = LocalMongo.matchesQuery userObject, achievement.get('query') + continue unless shouldBeAchieved # Could delete existing ones that shouldn't be achieved if we wanted. + earnedAchievement = _.find(alreadyEarnedAchievements, (ea) -> ea.get('user') is userID and ea.get('achievement') is achievement.get('_id').toHexString()) + if earnedAchievement + #console.log "... already earned #{achievement.get('name')} #{achievement.get('_id')} for user: #{user.get('name')} #{user.get('_id')}" if testUsers? + ++achievementsExisting + continue + #console.log "Making an achievement: #{achievement.get('name')} #{achievement.get('_id')} for user: #{user.get('name')} #{user.get('_id')}" if testUsers? + ++achievementsCreated + EarnedAchievement.createForAchievement achievement, user + + totalAchievementsExisting += achievementsExisting + totalAchievementsCreated += achievementsCreated + pctDone = (100 * usersFinished / total).toFixed(2) + console.log "Created #{achievementsCreated}, existing #{achievementsExisting} EarnedAchievements for #{user.get('name') or '???'} (#{user.get('_id')}) (#{pctDone}%, totals #{totalAchievementsExisting} existing, #{totalAchievementsCreated} created)" + doneWithUser() diff --git a/scripts/runAfterGit.coffee b/scripts/runAfterGit.coffee index 185dacfc8..ee1a5af30 100644 --- a/scripts/runAfterGit.coffee +++ b/scripts/runAfterGit.coffee @@ -14,7 +14,7 @@ May need an initial npm install upfront if newly checked out. \n ' if '--help' in process.argv #TODO: MD5 Verification, using http://54.91.159.37/dump.md5 using digest stream https://github.com/jeffbski/digest-stream -dbDump = 'http://54.91.159.37/dump.tar.gz' # Don't change this unless you know what you're doing +dbDump = 'http://analytics.codecombat.com:8080/dump.tar.gz' # Don't change this unless you know what you're doing dbLocalPath = '../temp' fs = require 'fs' diff --git a/scripts/vagrant/backup.bat b/scripts/vagrant/backup.bat new file mode 100644 index 000000000..430ce5a09 --- /dev/null +++ b/scripts/vagrant/backup.bat @@ -0,0 +1,3 @@ +@ECHO OFF +vagrant ssh -c "cd /vagrant/scripts/vagrant && bash backupMongo.sh" + diff --git a/scripts/vagrant/backup.sh b/scripts/vagrant/backup.sh new file mode 100755 index 000000000..ca91f65a3 --- /dev/null +++ b/scripts/vagrant/backup.sh @@ -0,0 +1,3 @@ +#!/bin/sh +vagrant ssh -c "cd /vagrant/scripts/vagrant && bash backupMongo.sh" + diff --git a/scripts/vagrant/backupMongo.sh b/scripts/vagrant/backupMongo.sh new file mode 100755 index 000000000..c7800ae7f --- /dev/null +++ b/scripts/vagrant/backupMongo.sh @@ -0,0 +1,8 @@ +#!/bin/bash +mkdir -p /vagrant/temp +cd /vagrant/temp +rm -fr backup +mkdir backup +cd backup +mongodump -db coco --collection users +mongodump -db coco --collection earnedachievements diff --git a/scripts/vagrant/brunch.bat b/scripts/vagrant/brunch.bat new file mode 100644 index 000000000..0e19d96d8 --- /dev/null +++ b/scripts/vagrant/brunch.bat @@ -0,0 +1,3 @@ +@ECHO OFF +vagrant ssh -c "cd /vagrant && bin/coco-brunch" + diff --git a/scripts/vagrant/brunch.sh b/scripts/vagrant/brunch.sh new file mode 100755 index 000000000..733bfc872 --- /dev/null +++ b/scripts/vagrant/brunch.sh @@ -0,0 +1,4 @@ +#!/bin/sh +vagrant ssh -c "sudo mount -o bind /node_modules /vagrant/node_modules" +vagrant ssh -c "cd /vagrant && BRUNCH_ENV=vagrant bin/coco-brunch" + diff --git a/scripts/vagrant/dev-server.bat b/scripts/vagrant/dev-server.bat new file mode 100644 index 000000000..18951a3c6 --- /dev/null +++ b/scripts/vagrant/dev-server.bat @@ -0,0 +1,3 @@ +@ECHO OFF +vagrant ssh -c "cd /vagrant && bin/coco-dev-server" + diff --git a/scripts/vagrant/dev-server.sh b/scripts/vagrant/dev-server.sh new file mode 100755 index 000000000..5e3bc1160 --- /dev/null +++ b/scripts/vagrant/dev-server.sh @@ -0,0 +1,4 @@ +#!/bin/sh +vagrant ssh -c "sudo mount -o bind /node_modules /vagrant/node_modules" +vagrant ssh -c "cd /vagrant && bin/coco-dev-server" + diff --git a/scripts/vagrant/fillMongo.sh b/scripts/vagrant/fillMongo.sh new file mode 100755 index 000000000..feb401064 --- /dev/null +++ b/scripts/vagrant/fillMongo.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Original content copyright (c) 2014 dpen2000 licensed under the MIT license +mkdir -p /vagrant/temp +cd /vagrant/temp +rm -f dump.tar.gz +rm -rf dump +wget http://analytics.codecombat.com:8080/dump.tar.gz +tar xzvf dump.tar.gz --no-same-owner +mongorestore --drop +if [ -d /vagrant/temp/backup ] +then + cd /vagrant/temp/backup + mongorestore +fi diff --git a/scripts/vagrant/provision.sh b/scripts/vagrant/provision.sh new file mode 100644 index 000000000..a97ed945a --- /dev/null +++ b/scripts/vagrant/provision.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Original content copyright (c) 2014 dpen2000 licensed under the MIT license + +# Set default ulimits +cat <<- EOF > /tmp/limits.conf +* soft nofile 10000 +* hard nofile 10000 +EOF +sudo mv /tmp/limits.conf /etc/security/limits.conf +sudo chown root:root /etc/security/limits.conf + +#Install required software +sudo apt-get -y update +sudo apt-get -y install python-software-properties git +sudo add-apt-repository -y ppa:chris-lea/node.js +sudo apt-get -y update +sudo apt-get -y install nodejs +sudo apt-get -y install g++ make +mkdir /vagrant/node_modules +sudo mkdir /node_modules +sudo chown vagrant:vagrant /node_modules +sudo mount -o bind /node_modules /vagrant/node_modules +cd /vagrant +sudo npm install +sudo npm install -g bower +sudo npm install -g brunch +sudo npm install -g geoip-lite +sudo npm install -g coffee-script +bower install --allow-root +sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 +echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list +sudo apt-get -y update +sudo apt-get -y install mongodb-org +sudo apt-get -y install ruby1.9.1 ruby1.9.1-dev +sudo gem install sass +npm install mongoose +bash /vagrant/scripts/vagrant/fillMongo.sh diff --git a/scripts/vagrant/update.bat b/scripts/vagrant/update.bat new file mode 100644 index 000000000..cc1c3d944 --- /dev/null +++ b/scripts/vagrant/update.bat @@ -0,0 +1,3 @@ +@ECHO OFF +vagrant ssh -c "cd /vagrant/scripts/vagrant && bash fillMongo.sh" + diff --git a/scripts/vagrant/update.sh b/scripts/vagrant/update.sh new file mode 100755 index 000000000..02eadbca4 --- /dev/null +++ b/scripts/vagrant/update.sh @@ -0,0 +1,3 @@ +#!/bin/sh +vagrant ssh -c "cd /vagrant/scripts/vagrant && bash fillMongo.sh" + diff --git a/scripts/windows/SCOCODE.bat b/scripts/windows/SCOCODE.bat index 55686d400..9b9a13b34 100755 --- a/scripts/windows/SCOCODE.bat +++ b/scripts/windows/SCOCODE.bat @@ -1,4 +1,6 @@ set "mongo_db_location=MONGO_DB_PATH_HERE" +call npm update +call bower update start cmd.exe cmd /c call nodemon -w server -w server_config.js start cmd.exe cmd /c call brunch w^ & mongod --setParameter textSearchEnabled=true^ diff --git a/scripts/windows/coco-dev-setup/batch/config/config.coco b/scripts/windows/coco-dev-setup/batch/config/config.coco index fee5f0b66..59a751db2 100755 --- a/scripts/windows/coco-dev-setup/batch/config/config.coco +++ b/scripts/windows/coco-dev-setup/batch/config/config.coco @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="ISO-8859-1" ?> <variables> - <version>3.5</version> + <version>3.7.1</version> <author>GlenDC</author> <copyright>CodeCombat.com © 2013-2014</copyright> <github_url>https://github.com/codecombat/codecombat.git</github_url> <github_ssh>git@github.com:codecombat/codecombat.git</github_ssh> - <database_backup>http://54.91.159.37/dump.tar.gz</database_backup> + <database_backup>http://analytics.codecombat.com:8080/dump.tar.gz</database_backup> </variables> diff --git a/scripts/windows/coco-dev-setup/batch/config/downloads.coco b/scripts/windows/coco-dev-setup/batch/config/downloads.coco index f8906cbb4..74f5d7e0d 100755 --- a/scripts/windows/coco-dev-setup/batch/config/downloads.coco +++ b/scripts/windows/coco-dev-setup/batch/config/downloads.coco @@ -2,44 +2,44 @@ <variables> <general> <b32> - <nodejs>http://nodejs.org/dist/v0.10.25/node-v0.10.25-x86.msi</nodejs> + <nodejs>http://nodejs.org/dist/v0.10.35/node-v0.10.35-x86.msi</nodejs> <ruby>http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.0.0-p353.exe?direct</ruby> <python>http://s3.amazonaws.com/CodeCombatLargeFiles/python-32.msi</python> <vs12redist>http://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x86.exe</vs12redist> </b32> <b64> - <nodejs>http://nodejs.org/dist/v0.10.25/x64/node-v0.10.25-x64.msi</nodejs> + <nodejs>http://nodejs.org/dist/v0.10.35/x64/node-v0.10.35-x64.msi</nodejs> <ruby>http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.0.0-p353-x64.exe?direct</ruby> <python>http://s3.amazonaws.com/CodeCombatLargeFiles/python-64.msi</python> <vs12redist>http://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe</vs12redist> </b64> <general> - <gitbash>https://msysgit.googlecode.com/files/Git-1.8.5.2-preview20131230.exe</gitbash> + <gitbash>https://github.com/msysgit/msysgit/releases/download/Git-1.9.5-preview20141217/Git-1.9.5-preview20141217.exe</gitbash> <vs10redist>http://download.microsoft.com/download/C/6/D/C6D0FD4E-9E53-4897-9B91-836EBA2AACD3/vcredist_x86.exe</vs10redist> </general> </general> <Win8> <b32> - <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-i386-2.6.0.zip</mongodb> + <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-i386-2.6.4.zip</mongodb> </b32> <b64> - <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.6.0.zip</mongodb> + <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.6.4.zip</mongodb> </b64> </Win8> <Win7> <b32> - <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-i386-2.6.0.zip</mongodb> + <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-i386-2.6.4.zip</mongodb> </b32> <b64> - <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.6.0.zip</mongodb> + <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.6.4.zip</mongodb> </b64> </Win7> <Vista> <b32> - <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-i386-2.6.0.zip</mongodb> + <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.6.4.zip</mongodb> </b32> <b64> - <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2.6.0.zip</mongodb> + <mongodb>https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2.6.4.zip</mongodb> </b64> </Vista> </variables> diff --git a/scripts/windows/coco-dev-setup/batch/config/localized/license.coco b/scripts/windows/coco-dev-setup/batch/config/localized/license.coco index 9b753bf10..dd6e77fde 100755 --- a/scripts/windows/coco-dev-setup/batch/config/localized/license.coco +++ b/scripts/windows/coco-dev-setup/batch/config/localized/license.coco @@ -3,8 +3,8 @@ The MIT License (MIT) Copyright (c) 2014 CodeCombat Inc. and other contributors -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN sCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/scripts/windows/coco-dev-setup/batch/config/localized/readme-es.coco b/scripts/windows/coco-dev-setup/batch/config/localized/readme-es.coco new file mode 100644 index 000000000..00d46c4ce --- /dev/null +++ b/scripts/windows/coco-dev-setup/batch/config/localized/readme-es.coco @@ -0,0 +1,29 @@ + _____ _ _____ _ _ + / __ \ | | / __ \ | | | | + | / \/ ___ __| | ___ | / \/ ___ _ __ ___ | |__ __ _| |_ + | | / _ \ / _` |/ _ \ | | / _ \| '_ ` _ \| '_ \ / _` | __| + | \__/\ (_) | (_| | __/ | \__/\ (_) | | | | | | |_) | (_| | |_ + \____/\___/ \__,_|\___| \____/\___/|_| |_| |_|_.__/ \__,_|\__| + +============================================================================= + +Felicidades, ahora eres parte dela comunidad de CodeCombat. +Ahora que el ambiente de desarrollo ha sido instalado, estas listo para comenzar +a contribuir y hacer este mundo un mejor lugar. + +Tienes preguntas o te gustaria conocernos? +Habla con nosotros en hipchat @ https://www.hipchat.com/g3plnOKqa + +Tambien puedes hablar con nosotros en nuestro foro. +El foro esta en @ http://discourse.codecombat.com/ + +Puedes leer sobre los ultimos cambios en nuestro blog. +El blog esta en @ http://blog.codecombat.com/ + +Por ultimo, puedes encontrar casi toda nuestra documentacion +e informacion en nuestra wiki @ https://github.com/codecombat/codecombat/wiki + +Esperemos que disfrutes tanto la comunidad como nosotros + + + - Nick, George, Scott, Michael, Jeremy and Glen diff --git a/scripts/windows/coco-dev-setup/batch/config/localized/readme.coco b/scripts/windows/coco-dev-setup/batch/config/localized/readme.coco index 40665c28c..8cdeb9a12 100755 --- a/scripts/windows/coco-dev-setup/batch/config/localized/readme.coco +++ b/scripts/windows/coco-dev-setup/batch/config/localized/readme.coco @@ -14,7 +14,7 @@ contributing and help us make this world a better place. Do you have questions or would you like to meet us? Talk with us on hipchat @ https://www.hipchat.com/g3plnOKqa -Another way to reach is, is by visiting our forum. +Another way to reach us, is by visiting our forum. You can find it @ http://discourse.codecombat.com/ You can read about the latest developments on our blog site. @@ -23,7 +23,7 @@ This one can be found @ http://blog.codecombat.com/ Last but not least, you can find most of our documentation and information on our wiki @ https://github.com/codecombat/codecombat/wiki -We hope you'll enjoy yourself within our community, just as much as us. +We hope you enjoy yourself within our community, just as much as we do. - Nick, George, Scott, Michael, Jeremy and Glen diff --git a/scripts/windows/coco-dev-setup/batch/config/localized/tips-es.coco b/scripts/windows/coco-dev-setup/batch/config/localized/tips-es.coco new file mode 100644 index 000000000..16c4c6ddb --- /dev/null +++ b/scripts/windows/coco-dev-setup/batch/config/localized/tips-es.coco @@ -0,0 +1,6 @@ + 1) Si tienes una pregunta, hazla con cuidado y detalles + 2) Esta instalacion esta en beta y puede tener errores + 3) Puedes reportar bugs en @ 'https://github.com/codecombat/codecombat/issues' + 4) Tienes preguntas o sugerencias? Habla con nosotros en HipChat @ https://www.hipchat.com/g3plnOKqa + + Puedes encontrar una guia paso a paso para esta instalacion en @ https://github.com/codecombat/codecombat/wiki/Setup-on-Windows:-a-step-by-step-guide diff --git a/scripts/windows/coco-dev-setup/batch/config/localized/tips.coco b/scripts/windows/coco-dev-setup/batch/config/localized/tips.coco index 896086692..acac713a7 100755 --- a/scripts/windows/coco-dev-setup/batch/config/localized/tips.coco +++ b/scripts/windows/coco-dev-setup/batch/config/localized/tips.coco @@ -1,7 +1,6 @@ - 1) When there is a question, please answer carefull and correct + 1) When there is a question, please answer carefully and correctly 2) This setup is still in beta and may contain bugs 3) You can report bugs @ 'https://github.com/codecombat/codecombat/issues' - 4) Having questions/suggestions? Talk with us on HipChat via CodeCombat.com + 4) Having questions/suggestions? Talk with us on HipChat @ https://www.hipchat.com/g3plnOKqa - You can find a step-by-step guide for this installation on our wiki. - github.com/codecombat/codecombat/wiki/Setup-on-Windows:-a-step-by-step-guide \ No newline at end of file + You can find a step-by-step guide for this installation at @ https://github.com/codecombat/codecombat/wiki/Setup-on-Windows:-a-step-by-step-guide diff --git a/scripts/windows/coco-dev-setup/batch/localization/en.coco b/scripts/windows/coco-dev-setup/batch/localization/en.coco index bef7c9ae6..c58ac5513 100755 --- a/scripts/windows/coco-dev-setup/batch/localization/en.coco +++ b/scripts/windows/coco-dev-setup/batch/localization/en.coco @@ -47,7 +47,7 @@ <opensource>CodeCombat is opensource, like you already know.</opensource> <online>All our sourcecode can be found online at Github.</online> <manual>You can choose to do the entire Git setup yourself.</manual> - <norec>However we recommend that you instead let us handle it instead.</norec> + <norec>However we recommend that you let us handle it instead.</norec> </intro> <skip> <question>Do you want to do the Local Git setup manually yourself?</question> @@ -69,7 +69,7 @@ <info>Please enter your github information, to configure your local repository.</info> <username>Username: </username> <password>Password: </password> - <process>Thank you... Configuring your local repistory right now...</process> + <process>Thank you... Configuring your local repository right now...</process> </config> </github> <switch> @@ -82,7 +82,7 @@ <binstall>Installing bower packages...</binstall> <sass>Installing sass...</sass> <npm>Installing npm...</npm> - <brnch>Starting brunch....</brnch> + <brnch>Starting brunch...</brnch> <mongodb>Setting up a MongoDB database for you...</mongodb> <db>Downloading the last version of the CodeCombat database...</db> <script>Preparing the automatic startup script for you...</script> @@ -105,4 +105,4 @@ <s5> 2) Now just open 'localhost:3000' in your prefered browser.</s5> <s6>That's it, you're now ready to start working on CodeCombat!</s6> </start> -</variables> \ No newline at end of file +</variables> diff --git a/scripts/windows/coco-dev-setup/batch/localization/es.coco b/scripts/windows/coco-dev-setup/batch/localization/es.coco new file mode 100644 index 000000000..e74ce9400 --- /dev/null +++ b/scripts/windows/coco-dev-setup/batch/localization/es.coco @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<variables> + <global> + <native>Espanol</native> + <description>Espanol</description> + <tips>Algunas sugerencias antes de comenzar la instalación:</tips> + <exit>Presiona cualquier tecla para salir...</exit> + </global> + <language> + <choosen>Has elegido Español como tu idioma.</choosen> + <feedback>De ahora en adelante te enviaremos nuestro feedback en Español.</feedback> + </language> + <license> + <s1>Para continuar con la instalación del ambiente de desarrollo</s1> + <s2>tendrás que leer y estar de acuerdo con la licencia:</s2> + <q1>¿Has leído toda la licencia y estás de acuerdo con ella?</q1> + <a1>Esta instalación no se puede completar sin un acuerdo.</a1> + <a2>La instalación y configuración del ambiente de CodeCombat ha sido cancelada.</a2> + </license> + <install> + <system> + <bit>-bit computadora detectada.</bit> + <prefix>El sistema operativo</prefix> + <sufix>ha sido detectado.</sufix> + <xp>No soportamos Windows XP, instalación cancelada.</xp> + </system> + <process> + <sks>¿Ya has instalado todo el software necesario para instalar CodeCombat?</sks> + <skq>Te recomendamos que respondas negativamente si no estás seguro.</skq> + <skc>Saltando la instalación del software...</skc> + <s1>CodeCombat no pudo ser desarrollo sin third-party software.</s1> + <s2>Por eso necesitas instalar este software,</s2> + <s3>para comenzar a contribuir a nuestra comunidad.</s3> + <s4>Cancela la instalación si ya tienes la aplicación.</s4> + <winpath>Asegúrate de seleccionar la opción que agrega la aplicación al Windows Path, si la opción está disponible.</winpath> + <prefix>¿Ya tienes la última versión </prefix> + <sufix>instalada?</sufix> + <downloading>está descargando...</downloading> + <installing>está instalando...</installing> + <unzipping>está descomprimiendo...</unzipping> + <cleaning>está limpiando...</cleaning> + <mongodbpath>Por favor especifica la ruta completa donde MongoDB debe ser isntalado</mongodbpath> + </process> + </install> + <github> + <intro> + <opensource>CodeCombat es opensource, como ya sabes.</opensource> + <online>Todo nuestro código puede ser encontrada online en GitHub.</online> + <manual>Tú puedes instalar Git por tu cuenta.</manual> + <norec>Pero te recomendamos que nos permitas hacerlo nosotros.</norec> + </intro> + <skip> + <question>¿Quieres instalar Git por tu cuenta?</question> + <consequence>Asegurate de haber configurado tu repositorio correctamente.</consequence> + <donotclose>No cierres estaventana por favor..</donotclose> + <wait>Cuando estés listo, presiona cualquier tecla para continuar...</wait> + </skip> + <process> + <path>Por favor escriba la ruta completa de tu repositorio de CodeCombat de Git: </path> + <checkout>Por favor escriba la ruta completa donde quieres que se instale el ambiente de desarrollo de CodeCombat</checkout> + <bashi>Estainstalación necesita Git Bash.</bashi> + <bashp64>Git bash está instalada por default en 'C:\Program Files (x86)\Git'.</bashp64> + <bashp32>Git bash está instalada por default en 'C:\Program Files\Git'.</bashp32> + <bashq>Por favor escriba la ruta completa donde git bash está instalada o presione enter si está en el lugar default</bashq> + <ssh>Quieres revisar el repositorio via ssh?</ssh> + </process> + <config> + <intro>Deberías haber hecho fork a CodeCombat at tu propia cuenta de GitHub...</intro> + <info>Por favor ingrese su información de github para configurar el repositorio local.</info> + <username>Usuario: </username> + <password>Contraseña: </password> + <process>Gracias... Configurando tu repositorio local ahora mismo...</process> + </config> + </github> + <switch> + <install>¡La instalación de tu ambiente local exitosa!</install> + <close>Ahora puedes cerrar esta instalación.</close> + <open>Después de esto, deberías abrir la configuración para configurar automáticamente tu ambiente...</open> + </switch> + <npm> + <install>Instalando bower, brunch, nodemon y sendwithus...</install> + <binstall>Instalando bower packages...</binstall> + <sass>Instalando sass...</sass> + <npm>Instalando npm...</npm> + <brnch>Iniciando brunch....</brnch> + <mongodb>Configurando una base de datos de MongoDB para ti...</mongodb> + <db>Descargando la última version de la base de datos de CodeCombat...</db> + <script>Preparando el scritp de inicio automático...</script> + <close>¡No cerrar!</close> + </npm> + <error> + <path>¿Esa ruta ya existe, quieres sobreescribirla?</path> + <exist>Esa ruta no existe. Intente de nuevo...</exist> + </error> + <end> + <succesfull> La configuración fue exitosa.</succesfull> + <thankyou>Muchas gracias por tu contribución y nos veremos pronto.</thankyou> + <readme>¿Quieres leer el README para más información?</readme> + </end> + <start> + <s1>Puedes iniciar el ambiente </s1> + <s2>en un instance</s2> + <s3> 1) Da doble clickk</s3> + <s4> y el ambiente de desarrollo iniciará.</s4> + <s5> 2) Ahora abre 'localhost:3000' en tu browser preferido.</s5> + <s6>¡Listo! Ya estás preparado para trabajar en CodeCombat</s6> + </start> +</variables> diff --git a/scripts/windows/coco-dev-setup/batch/localization/languages.coco b/scripts/windows/coco-dev-setup/batch/localization/languages.coco index 2f3e2fe0d..d8e468267 100755 --- a/scripts/windows/coco-dev-setup/batch/localization/languages.coco +++ b/scripts/windows/coco-dev-setup/batch/localization/languages.coco @@ -3,4 +3,5 @@ ru nl de zh-HANT -zh-HANS \ No newline at end of file +zh-HANS +es diff --git a/scripts/windows/coco-dev-setup/batch/scripts/nab_automatic_script.bat b/scripts/windows/coco-dev-setup/batch/scripts/nab_automatic_script.bat index 7048f0180..a0c1e7635 100755 --- a/scripts/windows/coco-dev-setup/batch/scripts/nab_automatic_script.bat +++ b/scripts/windows/coco-dev-setup/batch/scripts/nab_automatic_script.bat @@ -2,6 +2,8 @@ call print_dashed_seperator call get_local_text npm_script npm script echo %npm_script% -echo start cmd.exe cmd /c "TITLE CodeCombat.com - mongodb database & mongod --setParameter textSearchEnabled=true --dbpath %~2">%~1\SCOCODE.bat +echo call npm update>%~1\SCOCODE.bat +echo call bower update>%~1\SCOCODE.bat +echo start cmd.exe cmd /c "TITLE CodeCombat.com - mongodb database & mongod --setParameter --dbpath %~2">%~1\SCOCODE.bat echo start cmd.exe cmd /c "TITLE CodeCombat.com - nodemon server & nodemon index.js">>%~1\SCOCODE.bat echo start cmd.exe cmd /c "TITLE CodeCombat.com - brunch - live compiler & brunch w">>%~1\SCOCODE.bat \ No newline at end of file diff --git a/scripts/windows/install-mongodb.ps1 b/scripts/windows/install-mongodb.ps1 index 503872e99..abe6cc39f 100755 --- a/scripts/windows/install-mongodb.ps1 +++ b/scripts/windows/install-mongodb.ps1 @@ -2,9 +2,9 @@ Set-ExecutionPolicy RemoteSigned $mongoDbPath = "C:\MongoDB" $mongoDbConfigPath = "$mongoDbPath\mongod.cfg" -$url = "http://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.4.9.zip" +$url = "http://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-3.0.2.zip" $zipFile = "$mongoDbPath\mongo.zip" -$unzippedFolderContent ="$mongoDbPath\mongodb-win32-x86_64-2008plus-2.4.9" +$unzippedFolderContent ="$mongoDbPath\mongodb-win32-x86_64-2008plus-3.0.2.zip" if ((Test-Path -path $mongoDbPath) -eq $True) { @@ -36,4 +36,4 @@ Remove-Item $zipFile -recurse -force & $mongoDBPath\bin\mongod.exe --config $mongoDbConfigPath --install -& net start mongodb \ No newline at end of file +& net start mongodb diff --git a/server/achievements/Achievement.coffee b/server/achievements/Achievement.coffee index 33675e7ee..f6f26bbed 100644 --- a/server/achievements/Achievement.coffee +++ b/server/achievements/Achievement.coffee @@ -5,6 +5,7 @@ utils = require '../../app/core/utils' plugins = require('../plugins/plugins') AchievablePlugin = require '../plugins/achievements' TreemaUtils = require '../../bower_components/treema/treema-utils.js' +config = require '../../server_config' # `pre` and `post` are not called for update operations executed directly on the database, # including `Model.update`,`.findByIdAndUpdate`,`.findOneAndUpdate`, `.findOneAndRemove`,and `.findByIdAndRemove`.order @@ -13,7 +14,23 @@ TreemaUtils = require '../../bower_components/treema/treema-utils.js' AchievementSchema = new mongoose.Schema({ userField: String -}, {strict: false}) +}, {strict: false,read: config.mongo.readpref}) + +AchievementSchema.index( + { + _fts: 'text' + _ftsx: 1 + }, + { + name: 'search index' + sparse: true + weights: {name: 1} + default_language: 'english' + 'language_override': 'language' + 'textIndexVersion': 2 + }) +AchievementSchema.index({i18nCoverage: 1}, {name: 'translation coverage index', sparse: true}) +AchievementSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true}) AchievementSchema.methods.objectifyQuery = -> try @@ -31,7 +48,7 @@ AchievementSchema.methods.getExpFunction = -> return utils.functionCreators[func.kind](func.parameters) if func.kind of utils.functionCreators AchievementSchema.statics.jsonschema = jsonschema -AchievementSchema.statics.earnedAchievements = {} +AchievementSchema.statics.achievementCollections = {} # Reloads all achievements into memory. # TODO might want to tweak this to only load new achievements @@ -41,16 +58,21 @@ AchievementSchema.statics.loadAchievements = (done) -> query = Achievement.find({collection: {$ne: 'level.sessions'}}) query.exec (err, docs) -> _.each docs, (achievement) -> - category = achievement.get 'collection' - AchievementSchema.statics.earnedAchievements[category] = [] unless category of AchievementSchema.statics.earnedAchievements - AchievementSchema.statics.earnedAchievements[category].push achievement - done?(AchievementSchema.statics.earnedAchievements) + collection = achievement.get 'collection' + AchievementSchema.statics.achievementCollections[collection] ?= [] + if _.find AchievementSchema.statics.achievementCollections[collection], ((a) -> a.get('_id').toHexString() is achievement.get('_id').toHexString()) + log.warn "Uh oh, we tried to add another copy of the same achievement #{achievement.get('_id')} #{achievement.get('name')} to the #{collection} achievement list..." + else + AchievementSchema.statics.achievementCollections[collection].push achievement + unless achievement.get('query') + log.error "Uh oh, there is an achievement with an empty query: #{achievement}" + done?(AchievementSchema.statics.achievementCollections) AchievementSchema.statics.getLoadedAchievements = -> - AchievementSchema.statics.earnedAchievements + AchievementSchema.statics.achievementCollections AchievementSchema.statics.resetAchievements = -> - delete AchievementSchema.statics.earnedAchievements[category] for category of AchievementSchema.statics.earnedAchievements + delete AchievementSchema.statics.achievementCollections[collection] for collection of AchievementSchema.statics.achievementCollections # Queries are stored as JSON strings, objectify them upon loading AchievementSchema.post 'init', (doc) -> doc.objectifyQuery() @@ -60,11 +82,13 @@ AchievementSchema.pre 'save', (next) -> next() # Reload achievements upon save +# This is going to basically not work when there is more than one application server, right? AchievementSchema.post 'save', -> @constructor.loadAchievements() AchievementSchema.plugin(plugins.NamedPlugin) AchievementSchema.plugin(plugins.SearchablePlugin, {searchable: ['name']}) AchievementSchema.plugin plugins.TranslationCoveragePlugin +AchievementSchema.plugin plugins.PatchablePlugin module.exports = Achievement = mongoose.model('Achievement', AchievementSchema, 'achievements') diff --git a/server/achievements/EarnedAchievement.coffee b/server/achievements/EarnedAchievement.coffee index de4b981c7..6bcaca197 100644 --- a/server/achievements/EarnedAchievement.coffee +++ b/server/achievements/EarnedAchievement.coffee @@ -10,13 +10,13 @@ EarnedAchievementSchema = new mongoose.Schema({ }, {strict:false}) EarnedAchievementSchema.pre 'save', (next) -> - @set('changed', Date.now()) + @set('changed', new Date()) next() EarnedAchievementSchema.index({user: 1, achievement: 1}, {unique: true, name: 'earned achievement index'}) EarnedAchievementSchema.index({user: 1, changed: -1}, {name: 'latest '}) -EarnedAchievementSchema.statics.createForAchievement = (achievement, doc, originalDocObj, done) -> +EarnedAchievementSchema.statics.createForAchievement = (achievement, doc, originalDocObj=null, previouslyEarnedAchievement=null, done) -> User = require '../users/User' userObjectID = doc.get(achievement.get('userField')) userID = if _.isObject userObjectID then userObjectID.toHexString() else userObjectID # Standardize! Use strings, not ObjectId's @@ -27,18 +27,20 @@ EarnedAchievementSchema.statics.createForAchievement = (achievement, doc, origin achievementName: achievement.get 'name' earnedRewards: achievement.get 'rewards' - worth = achievement.get('worth') ? 10 + pointWorth = achievement.get('worth') ? 10 + gemWorth = achievement.get('rewards')?.gems ? 0 earnedPoints = 0 + earnedGems = 0 + wrapUp = (earnedAchievementDoc) -> # Update user's experience points - update = {$inc: {points: earnedPoints}} + update = {$inc: {points: earnedPoints, 'earned.gems': earnedGems}} for rewardType, rewards of achievement.get('rewards') ? {} - if rewardType is 'gems' - update.$inc['earned.gems'] = rewards if rewards - else if rewards.length + continue if rewardType is 'gems' + if rewards.length update.$addToSet ?= {} update.$addToSet["earned.#{rewardType}"] = $each: rewards - User.update {_id: userID}, update, {}, (err, count) -> + User.update {_id: mongoose.Types.ObjectId(userID)}, update, {}, (err, count) -> log.error err if err? done?(earnedAchievementDoc) @@ -46,29 +48,49 @@ EarnedAchievementSchema.statics.createForAchievement = (achievement, doc, origin if isRepeatable #log.debug 'Upserting repeatable achievement called \'' + (achievement.get 'name') + '\' for ' + userID proportionalTo = achievement.get 'proportionalTo' - originalAmount = if originalDocObj then util.getByPath(originalDocObj, proportionalTo) or 0 else 0 docObj = doc.toObject() - newAmount = docObj[proportionalTo] + newAmount = util.getByPath(docObj, proportionalTo) or 0 + updateEarnedAchievement = (originalAmount) -> + #console.log 'original amount is', originalAmount, 'and new amount is', newAmount, 'for', proportionalTo, 'with doc', docObj, 'and previously earned achievement amount', previouslyEarnedAchievement?.get('achievedAmount'), 'because we had originalDocObj', originalDocObj - if originalAmount isnt newAmount - expFunction = achievement.getExpFunction() - earned.notified = false - earned.achievedAmount = newAmount - earned.earnedPoints = (expFunction(newAmount) - expFunction(originalAmount)) * worth - earned.previouslyAchievedAmount = originalAmount - EarnedAchievement.update {achievement: earned.achievement, user: earned.user}, earned, {upsert: true}, (err) -> - return log.debug err if err? + if originalAmount isnt newAmount + expFunction = achievement.getExpFunction() + earned.notified = false + earned.achievedAmount = newAmount + #console.log 'earnedPoints is', (expFunction(newAmount) - expFunction(originalAmount)) * pointWorth, 'was', earned.earnedPoints, earned.previouslyAchievedAmount, 'got exp function for new amount', newAmount, expFunction(newAmount), 'for original amount', originalAmount, expFunction(originalAmount), 'with point worth', pointWorth + earnedPoints = earned.earnedPoints = (expFunction(newAmount) - expFunction(originalAmount)) * pointWorth + earnedGems = earned.earnedGems = (expFunction(newAmount) - expFunction(originalAmount)) * gemWorth + earned.previouslyAchievedAmount = originalAmount + EarnedAchievement.update {achievement: earned.achievement, user: earned.user}, earned, {upsert: true}, (err) -> + return log.error err if err? - earnedPoints = earned.earnedPoints - #log.debug earnedPoints - wrapUp() + wrapUp(new EarnedAchievement(earned)) + else + done?() + + if proportionalTo is 'simulatedBy' and newAmount > 0 and not previouslyEarnedAchievement and Math.random() < 0.1 + # Because things like simulatedBy get updated with $inc and not the post-save plugin hook, + # we (infrequently) fetch the previously earned achievement so we can really update. + EarnedAchievement.findOne {user: earned.user, achievement: earned.achievement}, (err, previouslyEarnedAchievement) -> + log.error err if err? + updateEarnedAchievement previouslyEarnedAchievement?.get('achievedAmount') or 0 + else if previouslyEarnedAchievement + updateEarnedAchievement previouslyEarnedAchievement.get('achievedAmount') or 0 + else if originalDocObj # This branch could get buggy if unchangedCopy tracking isn't working. + updateEarnedAchievement util.getByPath(originalDocObj, proportionalTo) or 0 + else + updateEarnedAchievement 0 else # not alreadyAchieved #log.debug 'Creating a new earned achievement called \'' + (achievement.get 'name') + '\' for ' + userID - earned.earnedPoints = worth + earned.earnedPoints = pointWorth + earned.earnedGems = gemWorth (new EarnedAchievement(earned)).save (err, doc) -> return log.error err if err? - earnedPoints = worth + earnedPoints = pointWorth + earnedGems = gemWorth wrapUp(doc) + User.saveActiveUser userID, "achievement" + module.exports = EarnedAchievement = mongoose.model('EarnedAchievement', EarnedAchievementSchema) diff --git a/server/achievements/achievement_handler.coffee b/server/achievements/achievement_handler.coffee index 50fd1b0a8..6377f73dc 100644 --- a/server/achievements/achievement_handler.coffee +++ b/server/achievements/achievement_handler.coffee @@ -22,18 +22,18 @@ class AchievementHandler extends Handler 'i18n' 'i18nCoverage' ] - + allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] jsonSchema = require '../../app/schemas/models/achievement.coffee' hasAccess: (req) -> - req.method in ['GET', 'PUT'] or req.user?.isAdmin() + req.method in ['GET', 'PUT'] or req.user?.isAdmin() or req.user?.isArtisan() hasAccessToDocument: (req, document, method=null) -> method = (method or req.method).toLowerCase() return true if method is 'get' - return true if req.user?.isAdmin() + return true if req.user?.isAdmin() or req.user?.isArtisan() return true if method is 'put' and @isJustFillingTranslations(req, document) return @@ -49,7 +49,7 @@ class AchievementHandler extends Handler super req, res delete: (req, res, slugOrID) -> - return @sendForbiddenError res unless req.user?.isAdmin() + return @sendForbiddenError res unless req.user?.isAdmin() or req.user?.isArtisan() @getDocumentForIdOrSlug slugOrID, (err, document) => # Check first return @sendDatabaseError(res, err) if err return @sendNotFoundError(res) unless document? @@ -57,4 +57,6 @@ class AchievementHandler extends Handler return @sendDatabaseError(res, err) if err @sendNoContent res + getNamesByIDs: (req, res) -> @getNamesByOriginals req, res, true + module.exports = new AchievementHandler() diff --git a/server/achievements/earned_achievement_handler.coffee b/server/achievements/earned_achievement_handler.coffee index 1f8dd4e89..5afba74c2 100644 --- a/server/achievements/earned_achievement_handler.coffee +++ b/server/achievements/earned_achievement_handler.coffee @@ -6,14 +6,19 @@ EarnedAchievement = require './EarnedAchievement' User = require '../users/User' Handler = require '../commons/Handler' LocalMongo = require '../../app/lib/LocalMongo' +util = require '../../app/core/utils' +LevelSession = require '../levels/sessions/LevelSession' +UserPollsRecord = require '../polls/UserPollsRecord' class EarnedAchievementHandler extends Handler modelClass: EarnedAchievement + editableProperties: ['notified'] + # Don't allow POSTs or anything yet hasAccess: (req) -> return false unless req.user - req.method in ['GET', 'POST'] # or req.user.isAdmin() + req.method in ['GET', 'POST', 'PUT'] # or req.user.isAdmin() get: (req, res) -> return @getByAchievementIDs(req, res) if req.query.view is 'get-by-achievement-ids' @@ -65,6 +70,10 @@ class EarnedAchievementHandler extends Handler return @sendNotFoundError(res, 'Could not find achievement.') else if not trigger return @sendNotFoundError(res, 'Could not find trigger.') + else if achievement.get('proportionalTo') and earned + EarnedAchievement.createForAchievement(achievement, trigger, null, earned, (earnedAchievementDoc) => + @sendCreated(res, (earnedAchievementDoc or earned)?.toObject()) + ) else if earned achievementEarned = achievement.get('rewards') actuallyEarned = earned.get('earnedRewards') @@ -82,11 +91,13 @@ class EarnedAchievementHandler extends Handler return @sendDatabaseError(res, err) if err return @sendSuccess(res, earned.toObject()) ) - else if achievement.get('proportionalTo') - return @sendBadInputError(res, 'Cannot currently do this to repeatable docs...') else - EarnedAchievement.createForAchievement(achievement, trigger, null, (earnedAchievementDoc) => - @sendCreated(res, earnedAchievementDoc.toObject()) + EarnedAchievement.createForAchievement(achievement, trigger, null, null, (earnedAchievementDoc) => + if earnedAchievementDoc + @sendCreated(res, earnedAchievementDoc.toObject()) + else + console.error "Couldn't create achievement", achievement, trigger + @sendNotFoundError res, "Couldn't create achievement" ) ) @@ -118,6 +129,7 @@ class EarnedAchievementHandler extends Handler res.send(earnedAchievements) recalculate: (req, res) -> + return @sendForbiddenError(res) unless req.user?.isAdmin() onSuccess = (data) => log.debug 'Finished recalculating achievements' if 'achievements' of req.body # Support both slugs and IDs separated by commas achievementSlugsOrIDs = req.body.achievements @@ -136,7 +148,12 @@ class EarnedAchievementHandler extends Handler recalculatingAll = true t0 = new Date().getTime() total = 100000 - User.count {anonymous:false}, (err, count) -> total = count + #testUsers = ['livelily+test37@gmail.com'] + if testUsers? + userQuery = emailLower: {$in: testUsers} + else + userQuery = anonymous: false + User.count userQuery, (err, count) -> total = count onFinished = -> t1 = new Date().getTime() @@ -157,7 +174,7 @@ class EarnedAchievementHandler extends Handler log.info "Recalculating a total of #{achievements.length} achievements..." # Fetch every single user. This tends to get big so do it in a streaming fashion. - userStream = User.find().sort('_id').stream() + userStream = User.find(userQuery).sort('_id').stream() streamFinished = false usersTotal = 0 usersFinished = 0 @@ -173,126 +190,140 @@ class EarnedAchievementHandler extends Handler userStream.on 'data', (user) -> ++usersTotal numberRunning += 1 + #return doneWithUser() if usersTotal / total < 0.0217 # If it died, we can skip ahead on restart like this. userStream.pause() if numberRunning > 20 # Keep track of a user's already achieved in order to set the notified values correctly userID = user.get('_id').toHexString() - # Fetch all of a user's earned achievements - EarnedAchievement.find {user: userID}, (err, alreadyEarned) -> - alreadyEarnedIDs = [] - previousPoints = 0 - previousRewards = heroes: [], items: [], levels: [], gems: 0 - async.each alreadyEarned, ((earned, doneWithEarned) -> - if (_.find achievements, (single) -> earned.get('achievement') is single.get('_id').toHexString()) # if already earned - alreadyEarnedIDs.push earned.get('achievement') - previousPoints += earned.get 'earnedPoints' - for rewardType in ['heroes', 'items', 'levels'] - previousRewards[rewardType] = previousRewards[rewardType].concat(earned.get('earnedRewards')?[rewardType] ? []) - previousRewards.gems += earned.get('earnedRewards')?.gems ? 0 - doneWithEarned() - ), (err) -> # After checking already achieved + # Fetch a user's poll record so we can get the gems they should have from that. + UserPollsRecord.findOne {user: userID}, (err, userPollsRecord) -> + log.error err if err + pollGems = 0 + for pollID, reward of userPollsRecord?.get('rewards') or {} + pollGems += Math.ceil 2 * reward.random * reward.level + + # Fetch all of a user's earned achievements + EarnedAchievement.find {user: userID}, (err, alreadyEarned) -> log.error err if err - # TODO maybe also delete earned? Make sure you don't delete too many - - newTotalPoints = 0 - newTotalRewards = heroes: [], items: [], levels: [], gems: 0 - - async.each achievements, ((achievement, doneWithAchievement) -> - isRepeatable = achievement.get('proportionalTo')? - model = mongoose.modelNameByCollection(achievement.get('collection')) - return doneWithAchievement new Error "Model with collection '#{achievement.get 'collection'}' doesn't exist." unless model? - - finalQuery = _.clone achievement.get 'query' - return doneWithAchievement() if _.isEmpty finalQuery - finalQuery.$or = [{}, {}] # Allow both ObjectIDs or hex string IDs - finalQuery.$or[0][achievement.userField] = userID - finalQuery.$or[1][achievement.userField] = mongoose.Types.ObjectId userID - - model.findOne finalQuery, (err, something) -> - return doneWithAchievement() if _.isEmpty something - - #log.debug "Matched an achievement: #{achievement.get 'name'} for #{user.get 'name'}" - - earned = - user: userID - achievement: achievement._id.toHexString() - achievementName: achievement.get 'name' - notified: achievement._id in alreadyEarnedIDs - - if isRepeatable - earned.achievedAmount = something.get(achievement.get 'proportionalTo') - earned.previouslyAchievedAmount = 0 - - expFunction = achievement.getExpFunction() - newPoints = expFunction(earned.achievedAmount) * achievement.get('worth') ? 10 - else - newPoints = achievement.get('worth') ? 10 - - earned.earnedPoints = newPoints - newTotalPoints += newPoints - - earned.earnedRewards = achievement.get('rewards') + alreadyEarnedIDs = [] + previousPoints = 0 + previousRewards = heroes: [], items: [], levels: [], gems: 0 + async.each alreadyEarned, ((earned, doneWithEarned) -> + if (_.find achievements, (single) -> earned.get('achievement') is single.get('_id').toHexString()) # if already earned + alreadyEarnedIDs.push earned.get('achievement') + '' + previousPoints += earned.get 'earnedPoints' for rewardType in ['heroes', 'items', 'levels'] - newTotalRewards[rewardType] = newTotalRewards[rewardType].concat(achievement.get('rewards')?[rewardType] ? []) - newTotalRewards.gems += achievement.get('rewards')?.gems ? 0 - - EarnedAchievement.update {achievement:earned.achievement, user:earned.user}, earned, {upsert: true}, (err) -> - doneWithAchievement err - ), (err) -> # Wrap up a user, save points + previousRewards[rewardType] = previousRewards[rewardType].concat(earned.get('earnedRewards')?[rewardType] ? []) + previousRewards.gems += earned.get('earnedRewards')?.gems ? 0 + doneWithEarned() + ), (err) -> # After checking already achieved log.error err if err - #console.log 'User', user.get('name'), 'had newTotalPoints', newTotalPoints, 'and newTotalRewards', newTotalRewards, 'previousRewards', previousRewards - return doneWithUser(user) unless newTotalPoints or newTotalRewards.gems or _.some(newTotalRewards, (r) -> r.length) -# log.debug "Matched a total of #{newTotalPoints} new points" -# log.debug "Incrementing score for these achievements with #{newTotalPoints - previousPoints}" - pointDelta = newTotalPoints - previousPoints - pctDone = (100 * usersFinished / total).toFixed(2) - console.log "Updated points to #{newTotalPoints} (#{if pointDelta < 0 then '' else '+'}#{pointDelta}) for #{user.get('name') or '???'} (#{user.get('_id')}) (#{pctDone}%)" - if recalculatingAll - update = {$set: {points: newTotalPoints, 'earned.gems': 0, 'earned.heroes': [], 'earned.items': [], 'earned.levels': []}} - else - update = {$inc: {points: pointDelta}} - secondUpdate = {} # In case we need to pull, then push. - for rewardType, rewards of newTotalRewards - updateKey = "earned.#{rewardType}" - if rewardType is 'gems' - if recalculatingAll - update.$set[updateKey] = rewards + # TODO maybe also delete earned? Make sure you don't delete too many + + newTotalPoints = 0 + newTotalRewards = heroes: [], items: [], levels: [], gems: 0 + + async.each achievements, ((achievement, doneWithAchievement) -> + isRepeatable = achievement.get('proportionalTo')? + model = mongoose.modelNameByCollection(achievement.get('collection')) + return doneWithAchievement new Error "Model with collection '#{achievement.get 'collection'}' doesn't exist." unless model? + + finalQuery = _.clone achievement.get 'query' + return doneWithAchievement() if _.isEmpty finalQuery + finalQuery.$or = [{}, {}] # Allow both ObjectIDs or hex string IDs + finalQuery.$or[0][achievement.userField] = userID + finalQuery.$or[1][achievement.userField] = mongoose.Types.ObjectId userID + + model.findOne finalQuery, (err, something) -> + return doneWithAchievement() if _.isEmpty something + + #log.debug "Matched an achievement: #{achievement.get 'name'} for #{user.get 'name'}" + + earned = + user: userID + achievement: achievement._id.toHexString() + achievementName: achievement.get 'name' + notified: achievement._id.toHexString() in alreadyEarnedIDs + + if isRepeatable + earned.achievedAmount = util.getByPath(something.toObject(), achievement.get 'proportionalTo') or 0 + earned.previouslyAchievedAmount = 0 + + expFunction = achievement.getExpFunction() + newPoints = expFunction(earned.achievedAmount) * achievement.get('worth') ? 10 + newGems = expFunction(earned.achievedAmount) * (achievement.get('rewards')?.gems ? 0) else - update.$inc[updateKey] = rewards - previousRewards.gems + newPoints = achievement.get('worth') ? 10 + newGems = achievement.get('rewards')?.gems ? 0 + + earned.earnedPoints = newPoints + newTotalPoints += newPoints + + earned.earnedRewards = achievement.get('rewards') + for rewardType in ['heroes', 'items', 'levels'] + newTotalRewards[rewardType] = newTotalRewards[rewardType].concat(achievement.get('rewards')?[rewardType] ? []) + if isRepeatable and earned.earnedRewards + earned.earnedRewards = _.clone earned.earnedRewards + earned.earnedRewards.gems = newGems + newTotalRewards.gems += newGems + + EarnedAchievement.update {achievement:earned.achievement, user:earned.user}, earned, {upsert: true}, (err) -> + doneWithAchievement err + ), (err) -> # Wrap up a user, save points + log.error err if err + #console.log 'User', user.get('name'), 'had newTotalPoints', newTotalPoints, 'and newTotalRewards', newTotalRewards, 'previousRewards', previousRewards + return doneWithUser(user) unless newTotalPoints or newTotalRewards.gems or _.some(newTotalRewards, (r) -> r.length) + #log.debug "Matched a total of #{newTotalPoints} new points" + #log.debug "Incrementing score for these achievements with #{newTotalPoints - previousPoints}" + pointDelta = newTotalPoints - previousPoints + pctDone = (100 * usersFinished / total).toFixed(2) + console.log "Updated points to #{newTotalPoints} (#{if pointDelta < 0 then '' else '+'}#{pointDelta}) for #{user.get('name') or '???'} (#{user.get('_id')}) (#{pctDone}%)" + if recalculatingAll + update = {$set: {points: newTotalPoints, 'earned.gems': 0, 'earned.heroes': [], 'earned.items': [], 'earned.levels': []}} else - if recalculatingAll - update.$set[updateKey] = _.uniq rewards + update = {$inc: {points: pointDelta}} + secondUpdate = {} # In case we need to pull, then push. + for rewardType, rewards of newTotalRewards + updateKey = "earned.#{rewardType}" + if rewardType is 'gems' + if recalculatingAll + update.$set[updateKey] = rewards + pollGems + else + update.$inc[updateKey] = rewards - previousRewards.gems + else + if recalculatingAll + update.$set[updateKey] = _.uniq rewards + else + previousCounts = _.countBy previousRewards[rewardType] + newCounts = _.countBy rewards + relevantRewards = _.union _.keys(previousCounts), _.keys(newCounts) + for reward in relevantRewards + [previousCount, newCount] = [previousCounts[reward], newCounts[reward]] + if newCount and not previousCount + update.$addToSet ?= {} + update.$addToSet[updateKey] ?= {$each: []} + update.$addToSet[updateKey].$each.push reward + else if previousCount and not newCount + # Might $pull $each also work here? + update.$pullAll ?= {} + update.$pullAll[updateKey] ?= [] + update.$pullAll[updateKey].push reward + if update.$addToSet?[updateKey] and update.$pullAll?[updateKey] + # Perform the update in two calls to avoid "MongoError: Cannot update 'earned.levels' and 'earned.levels' at the same time" + secondUpdate.$addToSet ?= {} + secondUpdate.$addToSet[updateKey] = update.$addToSet[updateKey] + delete update.$addToSet[updateKey] + delete update.$addToSet unless _.size update.$addToSet + #console.log 'recalculatingAll?', recalculatingAll, 'so update is', update, 'secondUpdate', secondUpdate + User.update {_id: userID}, update, {}, (err) -> + log.error err if err? + if _.size secondUpdate + User.update {_id: userID}, secondUpdate, {}, (err) -> + log.error err if err? + doneWithUser user else - previousCounts = _.countBy previousRewards[rewardType] - newCounts = _.countBy rewards - relevantRewards = _.union _.keys(previousCounts), _.keys(newCounts) - for reward in relevantRewards - [previousCount, newCount] = [previousCounts[reward], newCounts[reward]] - if newCount and not previousCount - update.$addToSet ?= {} - update.$addToSet[updateKey] ?= {$each: []} - update.$addToSet[updateKey].$each.push reward - else if previousCount and not newCount - # Might $pull $each also work here? - update.$pullAll ?= {} - update.$pullAll[updateKey] ?= [] - update.$pullAll[updateKey].push reward - if update.$addToSet?[updateKey] and update.$pullAll?[updateKey] - # Perform the update in two calls to avoid "MongoError: Cannot update 'earned.levels' and 'earned.levels' at the same time" - secondUpdate.$addToSet ?= {} - secondUpdate.$addToSet[updateKey] = update.$addToSet[updateKey] - delete update.$addToSet[updateKey] - delete update.$addToSet unless _.size update.$addToSet - #console.log 'recalculatingAll?', recalculatingAll, 'so update is', update, 'secondUpdate', secondUpdate - User.update {_id: userID}, update, {}, (err) -> - log.error err if err? - if _.size secondUpdate - User.update {_id: userID}, secondUpdate, {}, (err) -> - log.error err if err? doneWithUser user - else - doneWithUser user module.exports = new EarnedAchievementHandler() diff --git a/server/analytics/AnalyticsLogEvent.coffee b/server/analytics/AnalyticsLogEvent.coffee new file mode 100644 index 000000000..9882470dd --- /dev/null +++ b/server/analytics/AnalyticsLogEvent.coffee @@ -0,0 +1,51 @@ +log = require 'winston' +mongoose = require 'mongoose' +plugins = require '../plugins/plugins' +utils = require '../lib/utils' +http = require 'http' +config = require '../../server_config' + +AnalyticsLogEventSchema = new mongoose.Schema({ + u: mongoose.Schema.Types.ObjectId + e: Number # event analytics.string ID + p: mongoose.Schema.Types.Mixed + + # TODO: Remove these legacy properties after we stop querying for them (probably 30 days, ~2/16/15) + user: mongoose.Schema.Types.ObjectId + event: String + properties: mongoose.Schema.Types.Mixed +}, {strict: false}) + +AnalyticsLogEventSchema.index({event: 1, _id: -1}) +AnalyticsLogEventSchema.index({event: 1, 'properties.level': 1}) +AnalyticsLogEventSchema.index({user: 1, event: 1}) + +AnalyticsLogEventSchema.statics.logEvent = (user, event, properties={}) -> + unless user? + log.warn 'No user given to analytics logEvent.' + return + + doc = new AnalyticsLogEvent + user: user + event: event + properties: properties + if config.isProduction and not config.unittest + docString = JSON.stringify doc + headers = + "Content-Type":'application/json' + "Content-Length": docString.length + + options = + host: 'analytics.codecombat.com' + port: 80 + path: '/analytics' + method: 'POST' + headers: headers + req = http.request options, (res) -> + req.on 'error', (e) -> log.warn e + req.write(docString) + req.end() + else + doc.save() + +module.exports = AnalyticsLogEvent = mongoose.model('analytics.log.event', AnalyticsLogEventSchema) diff --git a/server/analytics/AnalyticsPerDay.coffee b/server/analytics/AnalyticsPerDay.coffee new file mode 100644 index 000000000..bcd726f06 --- /dev/null +++ b/server/analytics/AnalyticsPerDay.coffee @@ -0,0 +1,13 @@ +mongoose = require 'mongoose' + +AnalyticsPerDaySchema = new mongoose.Schema({ + d: {type: String} # yyyymmdd day, e.g. '20150123' + e: {type: Number} # event (analytics string ID from analytics.strings) + l: {type: Number} # level (analytics string ID from analytics.strings) + f: {type: Number} # filter (analytics string ID from analytics.strings) + fv: {type: Number} # filter value (analytics string ID from analytics.strings) + c: {type: Number} # count +}, {strict: false}) + +# TODO: Why can't we query against a collection with caps, like 'analytics.perDay'? +module.exports = AnalyticsPerDay = mongoose.model('analytics.perday', AnalyticsPerDaySchema) diff --git a/server/analytics/AnalyticsString.coffee b/server/analytics/AnalyticsString.coffee new file mode 100644 index 000000000..dee04b425 --- /dev/null +++ b/server/analytics/AnalyticsString.coffee @@ -0,0 +1,12 @@ +mongoose = require 'mongoose' + +# Auto-incrementing number _id +# http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/#auto-increment-optimistic-loop +# TODO: Why strict:false? + +AnalyticsStringSchema = new mongoose.Schema({ + _id: {type: Number} + v: {type: String} +}, {strict: false}) + +module.exports = AnalyticsString = mongoose.model('analytics.string', AnalyticsStringSchema) diff --git a/server/analytics/AnalyticsStripeInvoice.coffee b/server/analytics/AnalyticsStripeInvoice.coffee new file mode 100644 index 000000000..f5a5427a4 --- /dev/null +++ b/server/analytics/AnalyticsStripeInvoice.coffee @@ -0,0 +1,9 @@ +mongoose = require 'mongoose' + +AnalyticsStripeInvoiceSchema = new mongoose.Schema({ + _id: String + date: Number + properties: mongoose.Schema.Types.Mixed +}, {strict: false}) + +module.exports = AnalyticsStripeInvoice = mongoose.model('analytics.stripe.invoice', AnalyticsStripeInvoiceSchema) diff --git a/server/analytics/AnalyticsUsersActive.coffee b/server/analytics/AnalyticsUsersActive.coffee new file mode 100644 index 000000000..8182a596d --- /dev/null +++ b/server/analytics/AnalyticsUsersActive.coffee @@ -0,0 +1,13 @@ +mongoose = require 'mongoose' +plugins = require '../plugins/plugins' + +AnalyticsUsersActiveSchema = new mongoose.Schema({ + created: + type: Date + 'default': Date.now +}, {strict: false}) + +AnalyticsUsersActiveSchema.index({created: 1}) +AnalyticsUsersActiveSchema.index({creator: 1}) + +module.exports = AnalyticsUsersActive = mongoose.model('analytics.users.active', AnalyticsUsersActiveSchema) diff --git a/server/analytics/analytics_log_event_handler.coffee b/server/analytics/analytics_log_event_handler.coffee new file mode 100644 index 000000000..f31a17234 --- /dev/null +++ b/server/analytics/analytics_log_event_handler.coffee @@ -0,0 +1,315 @@ +log = require 'winston' +mongoose = require 'mongoose' +utils = require '../lib/utils' +AnalyticsLogEvent = require './AnalyticsLogEvent' +Campaign = require '../campaigns/Campaign' +Level = require '../levels/Level' +Handler = require '../commons/Handler' + +class AnalyticsLogEventHandler extends Handler + modelClass: AnalyticsLogEvent + jsonSchema: require '../../app/schemas/models/analytics_log_event' + editableProperties: [ + 'e' + 'p' + 'event' + 'properties' + ] + + hasAccess: (req) -> + req.method in ['POST'] or req.user?.isAdmin() + + makeNewInstance: (req) -> + instance = super(req) + instance.set('u', req.user._id) + # TODO: Remove 'user' after we stop querying for it (probably 30 days, ~2/16/15) + instance.set('user', req.user._id) + instance + + getByRelationship: (req, res, args...) -> + return @logEvent(req, res) if args[1] is 'log_event' + # TODO: Remove these APIs + # return @getLevelCompletionsBySlug(req, res) if args[1] is 'level_completions' + # return @getCampaignCompletionsBySlug(req, res) if args[1] is 'campaign_completions' + super(arguments...) + + logEvent: (req, res) -> + # Converts strings to string IDs where possible, and logs the event + user = req.user?._id + event = req.query.event or req.body.event + properties = req.query.properties or req.body.properties + @sendSuccess res # Return request immediately + AnalyticsLogEvent.logEvent user, event, properties + + getLevelCompletionsBySlug: (req, res) -> + # Returns an array of per-day level starts and finishes + # Parameters: + # slug - level slug + # startDay - Inclusive, optional, e.g. '2014-12-14' + # endDay - Exclusive, optional, e.g. '2014-12-16' + + # TODO: An uncached call can take over 50s locally + # TODO: mapReduce() was slower than find() + + levelSlug = req.query.slug or req.body.slug + startDay = req.query.startDay or req.body.startDay + endDay = req.query.endDay or req.body.endDay + + return @sendSuccess res, [] unless levelSlug? + + # log.warn "level_completions levelSlug='#{levelSlug}' startDay=#{startDay} endDay=#{endDay}" + + # Cache results for 1 day + @levelCompletionsCache ?= {} + @levelCompletionsCachedSince ?= new Date() + if (new Date()) - @levelCompletionsCachedSince > 86400 * 1000 # Dumb cache expiration + @levelCompletionsCache = {} + @levelCompletionsCachedSince = new Date() + cacheKey = levelSlug + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + return @sendSuccess res, levelCompletions if levelCompletions = @levelCompletionsCache[cacheKey] + + levelDateMap = {} + + # Build query + queryParams = {$and: [ + {$or: [{"event" : 'Started Level'}, {"event" : 'Saw Victory'}]} + ]} + queryParams["$and"].push _id: {$gte: utils.objectIdFromTimestamp(startDay + "T00:00:00.000Z")} if startDay? + queryParams["$and"].push _id: {$lt: utils.objectIdFromTimestamp(endDay + "T00:00:00.000Z")} if endDay? + + # Query stream is better for large results + # http://mongoosejs.com/docs/api.html#query_Query-stream + stream = AnalyticsLogEvent.find(queryParams).select('created event properties user').stream() + stream.on 'data', (item) => + # Build per-level-day started and finished counts + created = item.get('created').toISOString().substring(0, 10) + event = item.get('event') + properties = item.get('properties') + if properties.level? then level = properties.level.toLowerCase().replace new RegExp(' ', 'g'), '-' + else if properties.levelID? then level = properties.levelID + else return + user = item.get('user') + + # log.warn "level_completions data " + " " + created + " " + event + " " + level + + levelDateMap[level] ?= {} + levelDateMap[level][created] ?= {} + levelDateMap[level][created]['finished'] ?= {} + levelDateMap[level][created]['started'] ?= {} + if event is 'Saw Victory' then levelDateMap[level][created]['finished'][user] = true + else levelDateMap[level][created]['started'][user] = true + .on 'error', (err) => + return @sendDatabaseError res, err + .on 'close', () => + # Build list of level completions + # Cache every level, since we had to grab all this data anyway + completions = {} + for level of levelDateMap + completions[level] = [] + for created, item of levelDateMap[level] + completions[level].push + level: level + created: created + started: Object.keys(item.started).length + finished: Object.keys(item.finished).length + cacheKey = level + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + @levelCompletionsCache[cacheKey] = completions[level] + unless levelSlug of completions then completions[levelSlug] = [] + @sendSuccess res, completions[levelSlug] + + getCampaignCompletionsBySlug: (req, res) -> + # Returns a dictionary of per-campaign level starts, finishes, and drop-offs + # Drop-off: last started or finished level event + # Parameters: + # slugs - array of campaign slugs + # startDay - Inclusive, optional, e.g. '2014-12-14' + # endDay - Exclusive, optional, e.g. '2014-12-16' + + # TODO: Must be a better way to organize this series of database calls (campaigns, levels, analytics) + # TODO: An uncached call can take over 50s locally + # TODO: Returns all the campaigns + # TODO: Calculate overall campaign stats + # TODO: Assumes db campaign levels are in progression order. Should build this based on actual progression. + # TODO: Remove earliest duplicate event so our dropped counts will be more accurate. + + campaignSlug = req.query.slug or req.body.slug + startDay = req.query.startDay or req.body.startDay + endDay = req.query.endDay or req.body.endDay + + # log.warn "campaign_completions campaignSlug='#{campaignSlug}' startDay=#{startDay} endDay=#{endDay}" + + return @sendSuccess res, [] unless campaignSlug? + + # Cache results for 1 day + @campaignDropOffsCache ?= {} + @campaignDropOffsCachedSince ?= new Date() + if (new Date()) - @campaignDropOffsCachedSince > 86400 * 1000 # Dumb cache expiration + @campaignDropOffsCache = {} + @campaignDropOffsCachedSince = new Date() + cacheKey = campaignSlug + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + return @sendSuccess res, campaignDropOffs if campaignDropOffs = @campaignDropOffsCache[cacheKey] + + getCompletions = (campaigns, userProgression) => + # Calculate campaign drop off rates + # Input: + # campaigns - per-campaign dictionary of ordered level slugs + # userProgression - per-user event lists + + # Remove duplicate user events + for user of userProgression + userProgression[user] = _.uniq userProgression[user], false, (val, index, arr) -> val.event + val.level + + # Order user progression by created + for user of userProgression + userProgression[user].sort (a,b) -> if a.created < b.created then return -1 else 1 + + # Per-level start/drop/finish/drop + levelProgression = {} + for user of userProgression + for i in [0...userProgression[user].length] + event = userProgression[user][i].event + level = userProgression[user][i].level + levelProgression[level] ?= + started: 0 + startDropped: 0 + finished: 0 + finishDropped: 0 + if event is 'Started Level' + levelProgression[level].started++ + levelProgression[level].startDropped++ if i is userProgression[user].length - 1 + else if event is 'Saw Victory' + levelProgression[level].finished++ + levelProgression[level].finishDropped++ if i is userProgression[user].length - 1 + + # Put in campaign order + completions = {} + for level of levelProgression + for campaign of campaigns + if level in campaigns[campaign] + started = levelProgression[level].started + startDropped = levelProgression[level].startDropped + finished = levelProgression[level].finished + finishDropped = levelProgression[level].finishDropped + completions[campaign] ?= + levels: [] + # overall: + # started: 0, + # startDropped: 0, + # finished: 0, + # finishDropped: 0 + completions[campaign].levels.push + level: level + started: started + startDropped: startDropped + finished: finished + finishDropped: finishDropped + break + + # Sort level data by campaign order + for campaign of completions + completions[campaign].levels.sort (a, b) -> + if campaigns[campaign].indexOf(a.level) < campaigns[campaign].indexOf(b.level) then return -1 else 1 + + # Return all campaign data for simplicity + # Cache other individual campaigns too, since we have them + @campaignDropOffsCache[cacheKey] = completions + for campaign of completions + cacheKey = campaign + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + @campaignDropOffsCache[cacheKey] = completions + unless campaignSlug of completions then completions[campaignSlug] = levels: [] + @sendSuccess res, completions + + getUserEventData = (campaigns) => + # Gather user start and finish event data + # Input: + # campaigns - per-campaign dictionary of ordered level slugs + # Output: + # userProgression - per-user event lists + + userProgression = {} + + queryParams = {$and: [{$or: [ {"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}]} + queryParams["$and"].push _id: {$gte: utils.objectIdFromTimestamp(startDay + "T00:00:00.000Z")} if startDay? + queryParams["$and"].push _id: {$lt: utils.objectIdFromTimestamp(endDay + "T00:00:00.000Z")} if endDay? + + # Query stream is better for large results + # http://mongoosejs.com/docs/api.html#query_Query-stream + stream = AnalyticsLogEvent.find(queryParams).select('created event properties user').stream() + stream.on 'data', (item) => + created = item.get('created') + event = item.get('event') + if event is 'Saw Victory' + level = item.get('properties.level').toLowerCase().replace new RegExp(' ', 'g'), '-' + else + level = item.get('properties.levelID') + return unless level? + user = item.get('user') + userProgression[user] ?= [] + userProgression[user].push + created: created + event: event + level: level + .on 'error', (err) => + return @sendDatabaseError res, err + .on 'close', () => + getCompletions campaigns, userProgression + + getLevelData = (campaigns, campaignLevelIDs) => + # Get level data and replace levelIDs with level slugs in campaigns + # Input: + # campaigns - per-campaign dictionary of ordered levelIDs + # campaignLevelIDs - dictionary of all campaign levelIDs + # Output: + # campaigns - per-campaign dictionary of ordered level slugs + + Level.find({original: {$in: campaignLevelIDs}, "version.isLatestMajor": true, "version.isLatestMinor": true}).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + levelSlugMap = {} + for doc in documents + levelID = doc.get('original') + levelSlug = doc.get('name').toLowerCase().replace new RegExp(' ', 'g'), '-' + levelSlugMap[levelID] = levelSlug + + # Replace levelIDs with level slugs + for campaign of campaigns + mapFn = (item) -> levelSlugMap[item] + campaigns[campaign] = _.map campaigns[campaign], mapFn, @ + + getUserEventData campaigns + + getCampaignData = () => + # Get campaign data + # Output: + # campaigns - per-campaign dictionary of ordered levelIDs + # campaignLevelIDs - dictionary of all campaign levelIDs + + Campaign.find().exec (err, documents) => + if err? then return @sendDatabaseError res, err + + campaigns = {} + levelCampaignMap = {} + campaignLevelIDs = [] + for doc in documents + slug = doc.get('slug') + levels = doc.get('levels') + campaigns[slug] = [] + levelCampaignMap[slug] = {} + for levelID of levels + campaigns[slug].push levelID + campaignLevelIDs.push levelID + levelCampaignMap[levelID] = slug + + getLevelData campaigns, campaignLevelIDs + + getCampaignData() + +module.exports = new AnalyticsLogEventHandler() diff --git a/server/analytics/analytics_perday_handler.coffee b/server/analytics/analytics_perday_handler.coffee new file mode 100644 index 000000000..fc1fd2ed2 --- /dev/null +++ b/server/analytics/analytics_perday_handler.coffee @@ -0,0 +1,415 @@ +AnalyticsPerDay = require './AnalyticsPerDay' +AnalyticsString = require './AnalyticsString' +Campaign = require '../campaigns/Campaign' +Level = require '../levels/Level' +Handler = require '../commons/Handler' +log = require 'winston' + +class AnalyticsPerDayHandler extends Handler + modelClass: AnalyticsPerDay + jsonSchema: require '../../app/schemas/models/analytics_perday' + + hasAccess: (req) -> + req.user?.isAdmin() or false + + getByRelationship: (req, res, args...) -> + return @sendForbiddenError res unless @hasAccess req + return @getCampaignCompletionsBySlug(req, res) if args[1] is 'campaign_completions' + return @getLevelCompletionsBySlug(req, res) if args[1] is 'level_completions' + return @getLevelDropsBySlugs(req, res) if args[1] is 'level_drops' + return @getLevelHelpsBySlugs(req, res) if args[1] is 'level_helps' + return @getLevelSubscriptionsBySlugs(req, res) if args[1] is 'level_subscriptions' + super(arguments...) + + getCampaignCompletionsBySlug: (req, res) -> + # Send back an ordered array of level per-day starts and finishes + # Parameters: + # slug - campaign slug + # startDay - Inclusive, optional, YYYYMMDD e.g. '20141214' + # endDay - Exclusive, optional, YYYYMMDD e.g. '20141216' + + campaignSlug = req.query.slug or req.body.slug + startDay = req.query.startDay or req.body.startDay + endDay = req.query.endDay or req.body.endDay + + # log.warn "campaign_completions campaignSlug='#{campaignSlug}' startDay=#{startDay} endDay=#{endDay}" + + return @sendSuccess res, [] unless campaignSlug? + + # Cache results in app server memory for 1 day + @campaignCompletionsCache ?= {} + @campaignCompletionsCachedSince ?= new Date() + if (new Date()) - @campaignCompletionsCachedSince > 86400 * 1000 + @campaignCompletionsCache = {} + @campaignCompletionsCachedSince = new Date() + cacheKey = campaignSlug + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + return @sendSuccess res, completions if completions = @campaignCompletionsCache[cacheKey] + + getCompletions = (orderedLevelSlugs, levelStringIDSlugMap) => + # 3. Send back an array of level starts and finishes + # Input: + # orderedLevelSlugs - Ordered list of level slugs, used for sorting results + # levelStringIDSlugMap - Maps level string IDs to level slugs + + campaignLevelIDs = Object.keys(levelStringIDSlugMap) + + AnalyticsString.find({v: {$in: ['Started Level', 'Saw Victory', 'all']}}).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + for doc in documents + startEventID = doc._id if doc.v is 'Started Level' + finishEventID = doc._id if doc.v is 'Saw Victory' + filterEventID = doc._id if doc.v is 'all' + return @sendSuccess res, [] unless startEventID? and finishEventID? and filterEventID? + + queryParams = {$and: [ + {$or: [{e: startEventID}, {e: finishEventID}]}, + {f: filterEventID}, + {l: {$in: campaignLevelIDs}} + ]} + queryParams["$and"].push {d: {$gte: startDay}} if startDay? + queryParams["$and"].push {d: {$lt: endDay}} if endDay? + AnalyticsPerDay.find(queryParams).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + levelEventCounts = {} + for doc in documents + levelEventCounts[doc.l] ?= {} + levelEventCounts[doc.l][doc.d] ?= {} + levelEventCounts[doc.l][doc.d][doc.e] ?= 0 + levelEventCounts[doc.l][doc.d][doc.e] += doc.c + + completions = [] + for levelID of levelEventCounts + days = {} + for day of levelEventCounts[levelID] + days[day] = + started: levelEventCounts[levelID][day][startEventID] ? 0 + finished: levelEventCounts[levelID][day][finishEventID] ? 0 + completions.push + level: levelStringIDSlugMap[levelID] + days: days + completions.sort (a, b) -> orderedLevelSlugs.indexOf(a.level) - orderedLevelSlugs.indexOf(b.level) + + @campaignCompletionsCache[cacheKey] = completions + @sendSuccess res, completions + + getLevelData = (campaignLevels) => + # 2. Get ordered level slugs and string ID to level slug mapping + # Input: + # campaignLevels - array of Level IDs + + queryParams = {original: {$in: campaignLevels}, "version.isLatestMajor": true, "version.isLatestMinor": true} + Level.find(queryParams).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + # Save original level ID and slug in array for sorting + campaignOriginalSlugs = [] + for doc in documents + campaignOriginalSlugs.push + slug: _.str.slugify(doc.get('name')) + original: doc.get('original').toString() + + # Sort slugs against original levels from campaign + campaignOriginalSlugs.sort (a, b) -> + if campaignLevels.indexOf(a.original) < campaignLevels.indexOf(b.original) then -1 else 1 + + # Lookup analytics string IDs for level slugs + orderedLevelSlugs = [] + orderedLevelSlugs.push item.slug for item in campaignOriginalSlugs + AnalyticsString.find({v: {$in: orderedLevelSlugs}}).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + levelStringIDSlugMap = {} + levelStringIDSlugMap[doc._id] = doc.v for doc in documents + getCompletions orderedLevelSlugs, levelStringIDSlugMap + + # 1. Get campaign levels + Campaign.find({slug: campaignSlug}).exec (err, documents) => + if err? then return @sendDatabaseError res, err + campaignLevels = [] + campaignLevels.push level for level of doc.get('levels') for doc in documents + getLevelData campaignLevels + + getLevelDropsBySlugs: (req, res) -> + # Send back an array of level/drops + # Drops - Number of unique users for which this was the last level they played + # Parameters: + # slugs - level slugs + # startDay - Inclusive, optional, YYYYMMDD e.g. '20141214' + # endDay - Exclusive, optional, YYYYMMDD e.g. '20141216' + + levelSlugs = req.query.slugs or req.body.slugs + startDay = req.query.startDay or req.body.startDay + endDay = req.query.endDay or req.body.endDay + + # log.warn "level_drops levelSlugs='#{levelSlugs}' startDay=#{startDay} endDay=#{endDay}" + + return @sendSuccess res, [] unless levelSlugs? + + # Cache results in app server memory for 1 day + @levelDropsCache ?= {} + @levelDropsCachedSince ?= new Date() + if (new Date()) - @levelDropsCachedSince > 86400 * 1000 + @levelDropsCache = {} + @levelDropsCachedSince = new Date() + cacheKey = levelSlugs.join '' + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + return @sendSuccess res, drops if drops = @levelDropsCache[cacheKey] + + AnalyticsString.find({v: {$in: ['User Dropped', 'all'].concat(levelSlugs)}}).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + levelStringIDSlugMap = {} + for doc in documents + droppedEventID = doc._id if doc.v is 'User Dropped' + filterEventID = doc._id if doc.v is 'all' + levelStringIDSlugMap[doc._id] = doc.v if doc.v in levelSlugs + + return @sendSuccess res, [] unless droppedEventID? and filterEventID? + + queryParams = {$and: [ + {e: droppedEventID}, + {f: filterEventID}, + {l: {$in: Object.keys(levelStringIDSlugMap)}} + ]} + queryParams["$and"].push {d: {$gte: startDay}} if startDay? + queryParams["$and"].push {d: {$lt: endDay}} if endDay? + AnalyticsPerDay.find(queryParams).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + levelEventCounts = {} + for doc in documents + levelEventCounts[doc.l] ?= {} + levelEventCounts[doc.l][doc.e] ?= 0 + levelEventCounts[doc.l][doc.e] += doc.c + + drops = [] + for levelID of levelEventCounts + drops.push + level: levelStringIDSlugMap[levelID] + dropped: levelEventCounts[levelID][droppedEventID] ? 0 + + @levelDropsCache[cacheKey] = drops + @sendSuccess res, drops + + getLevelCompletionsBySlug: (req, res) -> + # Returns an array of per-day starts and finishes for given level + # Parameters: + # slug - level slug + # startDay - Inclusive, optional, YYYYMMDD e.g. '20141214' + # endDay - Exclusive, optional, YYYYMMDD e.g. '20141216' + + # TODO: Code is similar to getCampaignCompletionsBySlug + + levelSlug = req.query.slug or req.body.slug + startDay = req.query.startDay or req.body.startDay + endDay = req.query.endDay or req.body.endDay + + return @sendSuccess res, [] unless levelSlug? + + # log.warn "level_completions levelSlug='#{levelSlug}' startDay=#{startDay} endDay=#{endDay}" + + # Cache results in app server memory for 1 day + @levelCompletionsCache ?= {} + @levelCompletionsCachedSince ?= new Date() + if (new Date()) - @levelCompletionsCachedSince > 86400 * 1000 + @levelCompletionsCache = {} + @levelCompletionsCachedSince = new Date() + cacheKey = levelSlug + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + return @sendSuccess res, levelCompletions if levelCompletions = @levelCompletionsCache[cacheKey] + + AnalyticsString.find({v: {$in: ['Started Level', 'Saw Victory', 'all', levelSlug]}}).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + for doc in documents + startEventID = doc._id if doc.v is 'Started Level' + finishEventID = doc._id if doc.v is 'Saw Victory' + filterEventID = doc._id if doc.v is 'all' + levelID = doc._id if doc.v is levelSlug + return @sendSuccess res, [] unless startEventID? and finishEventID? and filterEventID? and levelID? + + queryParams = {$and: [{$or: [{e: startEventID}, {e: finishEventID}]},{f: filterEventID},{l: levelID}]} + queryParams["$and"].push {d: {$gte: startDay}} if startDay? + queryParams["$and"].push {d: {$lt: endDay}} if endDay? + AnalyticsPerDay.find(queryParams).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + dayEventCounts = {} + for doc in documents + day = doc.get('d') + eventID = doc.get('e') + count = doc.get('c') + dayEventCounts[day] ?= {} + dayEventCounts[day][eventID] = count + + completions = [] + for day of dayEventCounts + started = 0 + finished = 0 + for eventID of dayEventCounts[day] + eventID = parseInt eventID + started = dayEventCounts[day][eventID] if eventID is startEventID + finished = dayEventCounts[day][eventID] if eventID is finishEventID + completions.push + created: day + started: started + finished: finished + + @levelCompletionsCache[cacheKey] = completions + @sendSuccess res, completions + + getLevelHelpsBySlugs: (req, res) -> + # Send back an array of per-day level help buttons clicked and videos started + # Parameters: + # slugs - level slugs + # startDay - Inclusive, optional, YYYYMMDD e.g. '20141214' + # endDay - Exclusive, optional, YYYYMMDD e.g. '20141216' + + levelSlugs = req.query.slugs or req.body.slugs + startDay = req.query.startDay or req.body.startDay + endDay = req.query.endDay or req.body.endDay + + # log.warn "level_helps levelSlugs='#{levelSlugs}' startDay=#{startDay} endDay=#{endDay}" + + return @sendSuccess res, [] unless levelSlugs? + + # Cache results in app server memory for 1 day + @levelHelpsCache ?= {} + @levelHelpsCachedSince ?= new Date() + if (new Date()) - @levelHelpsCachedSince > 86400 * 1000 + @levelHelpsCache = {} + @levelHelpsCachedSince = new Date() + cacheKey = levelSlugs.join '' + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + return @sendSuccess res, helps if helps = @levelHelpsCache[cacheKey] + + findQueryParams = {v: {$in: ['Problem alert help clicked', 'Spell palette help clicked', 'Start help video', 'all'].concat(levelSlugs)}} + AnalyticsString.find(findQueryParams).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + levelStringIDSlugMap = {} + for doc in documents + alertHelpEventID = doc._id if doc.v is 'Problem alert help clicked' + palettteHelpEventID = doc._id if doc.v is 'Spell palette help clicked' + videoHelpEventID = doc._id if doc.v is 'Start help video' + filterEventID = doc._id if doc.v is 'all' + levelStringIDSlugMap[doc._id] = doc.v if doc.v in levelSlugs + + return @sendSuccess res, [] unless alertHelpEventID? and palettteHelpEventID? and videoHelpEventID? and filterEventID? + + queryParams = {$and: [ + {e: {$in: [alertHelpEventID, palettteHelpEventID, videoHelpEventID]}}, + {f: filterEventID}, + {l: {$in: Object.keys(levelStringIDSlugMap)}} + ]} + queryParams["$and"].push {d: {$gte: startDay}} if startDay? + queryParams["$and"].push {d: {$lt: endDay}} if endDay? + AnalyticsPerDay.find(queryParams).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + levelEventCounts = {} + for doc in documents + levelEventCounts[doc.l] ?= {} + levelEventCounts[doc.l][doc.d] ?= {} + levelEventCounts[doc.l][doc.d][doc.e] ?= 0 + levelEventCounts[doc.l][doc.d][doc.e] += doc.c + + helps = [] + for levelID of levelEventCounts + for day of levelEventCounts[levelID] + alertHelps = 0 + paletteHelps = 0 + videoStarts = 0 + for eventID of levelEventCounts[levelID][day] + alertHelps = levelEventCounts[levelID][day][eventID] if parseInt(eventID) is alertHelpEventID + paletteHelps = levelEventCounts[levelID][day][eventID] if parseInt(eventID) is palettteHelpEventID + videoStarts = levelEventCounts[levelID][day][eventID] if parseInt(eventID) is videoHelpEventID + helps.push + level: levelStringIDSlugMap[levelID] + day: day + alertHelps: alertHelps + paletteHelps: paletteHelps + videoStarts: videoStarts + + @levelHelpsCache[cacheKey] = helps + @sendSuccess res, helps + + + getLevelSubscriptionsBySlugs: (req, res) -> + # Send back an array of level subscriptions shown and purchased counts + # Parameters: + # slugs - level slugs + # startDay - Inclusive, optional, YYYYMMDD e.g. '20141214' + # endDay - Exclusive, optional, YYYYMMDD e.g. '20141216' + + levelSlugs = req.query.slugs or req.body.slugs + startDay = req.query.startDay or req.body.startDay + endDay = req.query.endDay or req.body.endDay + + # log.warn "level_subscriptions levelSlugs='#{levelSlugs}' startDay=#{startDay} endDay=#{endDay}" + + return @sendSuccess res, [] unless levelSlugs? + + # Cache results in app server memory for 1 day + @levelSubscriptionsCache ?= {} + @levelSubscriptionsCachedSince ?= new Date() + if (new Date()) - @levelSubscriptionsCachedSince > 86400 * 1000 + @levelSubscriptionsCache = {} + @levelSubscriptionsCachedSince = new Date() + cacheKey = levelSlugs.join '' + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + return @sendSuccess res, subscriptions if subscriptions = @levelSubscriptionsCache[cacheKey] + + findQueryParams = {v: {$in: ['Show subscription modal', 'Finished subscription purchase', 'all'].concat(levelSlugs)}} + AnalyticsString.find(findQueryParams).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + levelStringIDSlugMap = {} + for doc in documents + showSubEventID = doc._id if doc.v is 'Show subscription modal' + finishSubEventID = doc._id if doc.v is 'Finished subscription purchase' + filterEventID = doc._id if doc.v is 'all' + levelStringIDSlugMap[doc._id] = doc.v if doc.v in levelSlugs + + return @sendSuccess res, [] unless showSubEventID? and finishSubEventID? and filterEventID? + + queryParams = {$and: [ + {e: {$in: [showSubEventID, finishSubEventID]}}, + {f: filterEventID}, + {l: {$in: Object.keys(levelStringIDSlugMap)}} + ]} + queryParams["$and"].push {d: {$gte: startDay}} if startDay? + queryParams["$and"].push {d: {$lt: endDay}} if endDay? + AnalyticsPerDay.find(queryParams).exec (err, documents) => + if err? then return @sendDatabaseError res, err + + levelEventCounts = {} + for doc in documents + levelEventCounts[doc.l] ?= {} + levelEventCounts[doc.l][doc.e] ?= 0 + levelEventCounts[doc.l][doc.e] += doc.c + + subscriptions = [] + for levelID of levelEventCounts + subsShown = 0 + subsPurchased = 0 + for eventID of levelEventCounts[levelID] + subsShown = levelEventCounts[levelID][eventID] if parseInt(eventID) is showSubEventID + subsPurchased = levelEventCounts[levelID][eventID] if parseInt(eventID) is finishSubEventID + subscriptions.push + level: levelStringIDSlugMap[levelID] + shown: subsShown + purchased: subsPurchased + + @levelSubscriptionsCache[cacheKey] = subscriptions + @sendSuccess res, subscriptions + +module.exports = new AnalyticsPerDayHandler() diff --git a/server/analytics/analytics_string_handler.coffee b/server/analytics/analytics_string_handler.coffee new file mode 100644 index 000000000..cfd81a7bf --- /dev/null +++ b/server/analytics/analytics_string_handler.coffee @@ -0,0 +1,9 @@ +AnalyticsString = require './AnalyticsString' +Handler = require '../commons/Handler' + +class AnalyticsStringHandler extends Handler + modelClass: AnalyticsString + jsonSchema: require '../../app/schemas/models/analytics_string' + hasAccess: (req) -> req.method in ['GET'] or req.user?.isAdmin() + +module.exports = new AnalyticsStringHandler() diff --git a/server/analytics/analytics_stripe_invoice_handler.coffee b/server/analytics/analytics_stripe_invoice_handler.coffee new file mode 100644 index 000000000..fcf3a3c20 --- /dev/null +++ b/server/analytics/analytics_stripe_invoice_handler.coffee @@ -0,0 +1,20 @@ +Handler = require '../commons/Handler' +AnalyticsStripeInvoice = require './AnalyticsStripeInvoice' + +class AnalyticsStripeInvoiceHandler extends Handler + modelClass: AnalyticsStripeInvoice + jsonSchema: require '../../app/schemas/models/analytics_stripe_invoice' + + hasAccess: (req) -> req.user?.isAdmin() + + getByRelationship: (req, res, args...) -> + return @sendForbiddenError(res) unless @hasAccess(req) + return @getAll(req, res) if args[1] is 'all' + super(arguments...) + + getAll: (req, res) -> + AnalyticsStripeInvoice.find {}, (err, docs) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, docs) + +module.exports = new AnalyticsStripeInvoiceHandler() diff --git a/server/analytics/analytics_users_active_handler.coffee b/server/analytics/analytics_users_active_handler.coffee new file mode 100644 index 000000000..c223a62c7 --- /dev/null +++ b/server/analytics/analytics_users_active_handler.coffee @@ -0,0 +1,16 @@ +AnalyticsUsersActive = require './AnalyticsUsersActive' +Handler = require '../commons/Handler' + +class AnalyticsUsersActiveHandler extends Handler + modelClass: AnalyticsUsersActive + jsonSchema: require '../../app/schemas/models/analytics_users_active' + + hasAccess: (req) -> + req.method in ['GET'] or req.user?.isAdmin() + + makeNewInstance: (req) -> + instance = super(req) + instance.set('creator', req.user._id) + instance + +module.exports = new AnalyticsUsersActiveHandler() diff --git a/server/articles/Article.coffee b/server/articles/Article.coffee index 1645b15b6..cff703cd2 100644 --- a/server/articles/Article.coffee +++ b/server/articles/Article.coffee @@ -1,11 +1,39 @@ mongoose = require 'mongoose' plugins = require '../plugins/plugins' +config = require '../../server_config' -ArticleSchema = new mongoose.Schema(body: String, {strict: false}) +ArticleSchema = new mongoose.Schema(body: String, {strict: false,read:config.mongo.readpref}) + +ArticleSchema.index( + { + index: 1 + _fts: 'text' + _ftsx: 1 + }, + { + name: 'search index' + sparse: true + weights: {body: 1, name: 1} + default_language: 'english' + 'language_override': 'searchLanguage' + 'textIndexVersion': 2 + }) +ArticleSchema.index( + { + original: 1 + 'version.major': -1 + 'version.minor': -1 + }, + { + name: 'version index' + unique: true + }) +ArticleSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true}) ArticleSchema.plugin(plugins.NamedPlugin) ArticleSchema.plugin(plugins.VersionedPlugin) ArticleSchema.plugin(plugins.SearchablePlugin, {searchable: ['body', 'name']}) +ArticleSchema.plugin(plugins.TranslationCoveragePlugin) ArticleSchema.plugin(plugins.PatchablePlugin) module.exports = mongoose.model('article', ArticleSchema) diff --git a/server/articles/article_handler.coffee b/server/articles/article_handler.coffee index d0d500904..05dcb4139 100644 --- a/server/articles/article_handler.coffee +++ b/server/articles/article_handler.coffee @@ -7,10 +7,10 @@ ArticleHandler = class ArticleHandler extends Handler jsonSchema: require '../../app/schemas/models/article' hasAccess: (req) -> - req.method is 'GET' or req.user?.isAdmin() + req.method is 'GET' or req.user?.isAdmin() or req.user?.isArtisan() hasAccessToDocument: (req, document, method=null) -> - return true if req.method is 'GET' or method is 'get' or req.user?.isAdmin() + return true if req.method is 'GET' or method is 'get' or req.user?.isAdmin() or req.user?.isArtisan() return false module.exports = new ArticleHandler() diff --git a/server/campaigns/Campaign.coffee b/server/campaigns/Campaign.coffee new file mode 100644 index 000000000..65a7ef614 --- /dev/null +++ b/server/campaigns/Campaign.coffee @@ -0,0 +1,37 @@ +mongoose = require 'mongoose' +plugins = require '../plugins/plugins' +log = require 'winston' +config = require '../../server_config' + +CampaignSchema = new mongoose.Schema(body: String, {strict: false,read:config.mongo.readpref}) + +CampaignSchema.index({i18nCoverage: 1}, {name: 'translation coverage index', sparse: true}) +CampaignSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true}) + +CampaignSchema.plugin(plugins.NamedPlugin) +CampaignSchema.plugin(plugins.TranslationCoveragePlugin) +CampaignSchema.plugin plugins.PatchablePlugin + +CampaignSchema.statics.updateAdjacentCampaigns = (savedCampaign) -> + Campaign = require '../campaigns/Campaign' + query = {} + query["adjacentCampaigns.#{savedCampaign.get '_id'}"] = {$exists: true} + Campaign.find(query).exec (err, campaigns) -> + return log.error "Couldn't search for adjacent campaigns to update because of #{err}" if err + for campaign in campaigns + acs = campaign.get 'adjacentCampaigns' + ac = acs[savedCampaign.get '_id'] + # Let's make sure that we're adding translations, otherwise let's not update yet. + # We could possibly remove this; not sure it's worth having. + [oldI18NCount, newI18NCount] = [0, 0] + oldI18NCount += _.size(translations) for lang, translations of ac.i18n ? {} + newI18NCount += _.size(translations) for lang, translations of savedCampaign.get('i18n') ? {} + continue unless newI18NCount > oldI18NCount + ac.i18n = savedCampaign.get('i18n') + # Save without using middleware so that we don't get into a post-save loop. + Campaign.findByIdAndUpdate campaign._id, {$set: {adjacentCampaigns: acs}}, (err, doc) -> + return log.error "Couldn't save updated adjacent campaign because of #{err}" if err + +CampaignSchema.post 'save', -> @constructor.updateAdjacentCampaigns @ + +module.exports = mongoose.model('campaign', CampaignSchema) diff --git a/server/campaigns/campaign_handler.coffee b/server/campaigns/campaign_handler.coffee new file mode 100644 index 000000000..6b40def2e --- /dev/null +++ b/server/campaigns/campaign_handler.coffee @@ -0,0 +1,105 @@ +Campaign = require './Campaign' +Level = require '../levels/Level' +Achievement = require '../achievements/Achievement' +Handler = require '../commons/Handler' +async = require 'async' +mongoose = require 'mongoose' + +CampaignHandler = class CampaignHandler extends Handler + modelClass: Campaign + editableProperties: [ + 'name' + 'fullName' + 'description' + 'i18n' + 'i18nCoverage' + 'ambientSound' + 'backgroundImage' + 'backgroundColor' + 'backgroundColorTransparent' + 'adjacentCampaigns' + 'levels' + ] + jsonSchema: require '../../app/schemas/models/campaign.schema' + + hasAccess: (req) -> + req.method in ['GET', 'PUT'] or req.user?.isAdmin() + + hasAccessToDocument: (req, document, method=null) -> + return true if req.user?.isAdmin() + + if @modelClass.schema.uses_coco_translation_coverage and (method or req.method).toLowerCase() in ['post', 'put'] + return true if @isJustFillingTranslations(req, document) + + if req.method is 'GET' + return true + + return false + + get: (req, res) -> + return @sendForbiddenError(res) if not @hasAccess(req) + # We don't have normal text search or anything set up to make /db/campaign work, so we'll just give them all campaigns, no problem. + q = @modelClass.find {} + q.exec (err, documents) => + return @sendDatabaseError(res, err) if err + documents = (@formatEntity(req, doc) for doc in documents) + @sendSuccess(res, documents) + + getByRelationship: (req, res, args...) -> + relationship = args[1] + if relationship in ['levels', 'achievements'] + projection = {} + if req.query.project + projection[field] = 1 for field in req.query.project.split(',') + @getDocumentForIdOrSlug args[0], (err, campaign) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless campaign? + return @getRelatedLevels(req, res, campaign, projection) if relationship is 'levels' + return @getRelatedAchievements(req, res, campaign, projection) if relationship is 'achievements' + else + super(arguments...) + + getRelatedLevels: (req, res, campaign, projection) -> + extraProjectionProps = [] + unless _.isEmpty(projection) + # Make sure that permissions and version are fetched, but not sent back if they didn't ask for them. + extraProjectionProps.push 'permissions' unless projection.permissions + extraProjectionProps.push 'version' unless projection.version + projection.permissions = 1 + projection.version = 1 + + levels = campaign.get('levels') or [] + + f = (levelOriginal) -> + (callback) -> + query = { original: mongoose.Types.ObjectId(levelOriginal) } + sort = { 'version.major': -1, 'version.minor': -1 } + Level.findOne(query, projection).sort(sort).exec callback + + fetches = (f(level.original) for level in _.values(levels)) + async.parallel fetches, (err, levels) => + return @sendDatabaseError(res, err) if err + filteredLevels = (_.omit(level.toObject(), extraProjectionProps) for level in levels) + return @sendSuccess(res, filteredLevels) + + getRelatedAchievements: (req, res, campaign, projection) -> + levels = campaign.get('levels') or [] + + f = (levelOriginal) -> + (callback) -> + query = { related: levelOriginal } + Achievement.find(query, projection).exec callback + + fetches = (f(level.original) for level in _.values(levels)) + async.parallel fetches, (err, achievementses) => + achievements = _.flatten(achievementses) + return @sendDatabaseError(res, err) if err + return @sendSuccess(res, (achievement.toObject() for achievement in achievements)) + + onPutSuccess: (req, doc) -> + docLink = "http://codecombat.com#{req.headers['x-current-path']}" + @sendChangedHipChatMessage creator: req.user, target: doc, docLink: docLink + + getNamesByIDs: (req, res) -> @getNamesByOriginals req, res, true + +module.exports = new CampaignHandler() diff --git a/server/clans/Clan.coffee b/server/clans/Clan.coffee new file mode 100644 index 000000000..8749055d9 --- /dev/null +++ b/server/clans/Clan.coffee @@ -0,0 +1,32 @@ +mongoose = require 'mongoose' +log = require 'winston' +config = require '../../server_config' +plugins = require '../plugins/plugins' +User = require '../users/User' +jsonSchema = require '../../app/schemas/models/clan.schema' + +ClanSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref} + +ClanSchema.pre 'save', (next) -> + User.update {_id: @get('ownerID')}, {$addToSet: {clans: @get('_id')}}, (err) => + if err + log.error err + return next(err) + next() + +ClanSchema.statics.privateProperties = [] +ClanSchema.statics.editableProperties = [ + 'description' + 'members' + 'name' + 'type' +] + +ClanSchema.plugin plugins.NamedPlugin + +# TODO: Do we need this? +# ClanSchema.plugin plugins.SearchablePlugin, {searchable: ['name']} + +ClanSchema.statics.jsonSchema = jsonSchema + +module.exports = Clan = mongoose.model 'clan', ClanSchema, 'clans' diff --git a/server/clans/clan_handler.coffee b/server/clans/clan_handler.coffee new file mode 100644 index 000000000..58c4a7ec6 --- /dev/null +++ b/server/clans/clan_handler.coffee @@ -0,0 +1,172 @@ +async = require 'async' +mongoose = require 'mongoose' +Handler = require '../commons/Handler' +AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent' +Clan = require './Clan' +EarnedAchievement = require '../achievements/EarnedAchievement' +EarnedAchievementHandler = require '../achievements/earned_achievement_handler' +LevelSession = require '../levels/sessions/LevelSession' +LevelSessionHandler = require '../levels/sessions/level_session_handler' +User = require '../users/User' +UserHandler = require '../users/user_handler' + +ClanHandler = class ClanHandler extends Handler + modelClass: Clan + jsonSchema: require '../../app/schemas/models/clan.schema' + allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'] + + hasAccess: (req) -> + return true if req.method is 'GET' + return false if req.method is 'POST' and req.body?.type is 'private' and not req.user?.isPremium() + req.method in @allowedMethods or req.user?.isAdmin() + + hasAccessToDocument: (req, document, method=null) -> + return false unless document? + return true if req.user?.isAdmin() + return true if (method or req.method).toLowerCase() is 'get' + return true if document.get('ownerID')?.equals req.user?._id + false + + makeNewInstance: (req) -> + instance = super(req) + instance.set 'ownerID', req.user._id + instance.set 'members', [req.user._id] + instance.set 'dashboardType', 'premium' if req.body?.type is 'private' + instance + + delete: (req, res, clanID) -> + @getDocumentForIdOrSlug clanID, (err, clan) => + return @sendDatabaseError res, err if err + return @sendNotFoundError res unless clan + return @sendForbiddenError res unless @hasAccessToDocument(req, clan) + memberIDs = clan.get('members') + Clan.remove {_id: clan.get('_id')}, (err) => + return @sendDatabaseError res, err if err + User.update {_id: {$in: memberIDs}}, {$pull: {clans: clan.get('_id')}}, {multi: true}, (err) => + return @sendDatabaseError(res, err) if err + @sendNoContent(res) + AnalyticsLogEvent.logEvent req.user, 'Clan deleted', clanID: clanID, type: clan.get('type') + + getByRelationship: (req, res, args...) -> + return @joinClan(req, res, args[0]) if args[1] is 'join' + return @leaveClan(req, res, args[0]) if args[1] is 'leave' + return @getMemberAchievements(req, res, args[0]) if args[1] is 'member_achievements' + return @getMembers(req, res, args[0]) if args[1] is 'members' + return @getMemberSessions(req, res, args[0]) if args[1] is 'member_sessions' + return @getPublicClans(req, res) if args[1] is 'public' + return @removeMember(req, res, args[0], args[2]) if args.length is 3 and args[1] is 'remove' + super(arguments...) + + joinClan: (req, res, clanID) -> + return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous() + try + clanID = mongoose.Types.ObjectId(clanID) + catch err + return @sendNotFoundError(res, err) + Clan.findById clanID, (err, clan) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless clan + return @sendDatabaseError(res, err) unless clanType = clan.get('type') + return @sendForbiddenError(res) unless clanType is 'public' or req.user.isPremium() + Clan.update {_id: clanID}, {$addToSet: {members: req.user._id}}, (err) => + return @sendDatabaseError(res, err) if err + User.update {_id: req.user._id}, {$addToSet: {clans: clanID}}, (err) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res) + AnalyticsLogEvent.logEvent req.user, 'Clan joined', clanID: clanID, type: clanType + + leaveClan: (req, res, clanID) -> + return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous() + try + clanID = mongoose.Types.ObjectId(clanID) + catch err + return @sendNotFoundError(res, err) + Clan.findById clanID, (err, clan) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless clan + return @sendForbiddenError(res) if clan.get('ownerID')?.equals req.user._id + Clan.update {_id: clanID}, {$pull: {members: req.user._id}}, (err) => + return @sendDatabaseError(res, err) if err + User.update {_id: req.user._id}, {$pull: {clans: clanID}}, (err) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res) + AnalyticsLogEvent.logEvent req.user, 'Clan left', clanID: clanID, type: clan.get('type') + + getMemberAchievements: (req, res, clanID) -> + # TODO: add tests + memberLimit = 200 + Clan.findById clanID, (err, clan) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless clan + memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID + User.find {_id: {$in: memberIDs}}, 'nameLower', {sort: {nameLower: 1}}, (err, users) => + return @sendDatabaseError(res, err) if err + memberIDs = [] + for user in users + memberIDs.push user.id + break unless memberIDs.length < memberLimit + EarnedAchievement.find {user: {$in: memberIDs}}, 'achievementName user', (err, documents) => + return @sendDatabaseError(res, err) if err? + cleandocs = (EarnedAchievementHandler.formatEntity(req, doc) for doc in documents) + @sendSuccess(res, cleandocs) + + getMembers: (req, res, clanID) -> + # TODO: add tests + Clan.findById clanID, (err, clan) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless clan + memberIDs = clan.get('members') ? [] + User.find {_id: {$in: memberIDs}}, 'name nameLower points heroConfig.thangType', {sort: {nameLower: 1}}, (err, users) => + return @sendDatabaseError(res, err) if err + cleandocs = (UserHandler.formatEntity(req, doc) for doc in users) + @sendSuccess(res, cleandocs) + + getMemberSessions: (req, res, clanID) -> + # TODO: add tests + # TODO: restrict information returned based on clan type + memberLimit = 200 + Clan.findById clanID, (err, clan) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless clan + memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID + User.find {_id: {$in: memberIDs}}, 'name', {sort: {name: 1}}, (err, users) => + return @sendDatabaseError(res, err) if err + memberIDs = [] + for user in users + memberIDs.push user.id + break unless memberIDs.length < memberLimit + LevelSession.find {creator: {$in: memberIDs}}, 'changed codeLanguage creator creatorName levelID levelName playtime state submittedCodeLanguage', (err, documents) => + return @sendDatabaseError(res, err) if err? + cleandocs = (LevelSessionHandler.formatEntity(req, doc) for doc in documents) + @sendSuccess(res, cleandocs) + + getPublicClans: (req, res) -> + # Return 100 public clans, sorted by member count, created date + query = [{ $match : {type : 'public'} }] + query.push {$project : {_id: 1, name: 1, slug: 1, type: 1, description: 1, members: 1, memberCount: {$size: "$members"}, ownerID: 1}} + query.push {$sort: { memberCount: -1, _id: -1 }} + query.push {$limit: 100} + Clan.aggregate(query).exec (err, documents) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, documents) + + removeMember: (req, res, clanID, memberID) -> + return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous() + try + clanID = mongoose.Types.ObjectId(clanID) + memberID = mongoose.Types.ObjectId(memberID) + catch err + return @sendNotFoundError(res, err) + Clan.findById clanID, (err, clan) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless clan + return @sendForbiddenError res unless @hasAccessToDocument(req, clan) + return @sendForbiddenError(res) if clan.get('ownerID').equals memberID + Clan.update {_id: clanID}, {$pull: {members: memberID}}, (err) => + return @sendDatabaseError(res, err) if err + User.update {_id: memberID}, {$pull: {clans: clanID}}, (err) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res) + AnalyticsLogEvent.logEvent req.user, 'Clan member removed', clanID: clanID, type: clan.get('type'), memberID: memberID + +module.exports = new ClanHandler() diff --git a/server/commons/Handler.coffee b/server/commons/Handler.coffee index 62dced867..a2bcc60ad 100644 --- a/server/commons/Handler.coffee +++ b/server/commons/Handler.coffee @@ -23,18 +23,18 @@ module.exports = class Handler allowedMethods: ['GET', 'POST', 'PUT', 'PATCH'] constructor: -> - # TODO The second 'or' is for backward compatibility only is for backward compatibility only - @privateProperties = @modelClass.privateProperties or @privateProperties or [] - @editableProperties = @modelClass.editableProperties or @editableProperties or [] - @postEditableProperties = @modelClass.postEditableProperties or @postEditableProperties or [] - @jsonSchema = @modelClass.jsonSchema or @jsonSchema or {} + # TODO The second 'or' is for backward compatibility only + @privateProperties = @modelClass?.privateProperties or @privateProperties or [] + @editableProperties = @modelClass?.editableProperties or @editableProperties or [] + @postEditableProperties = @modelClass?.postEditableProperties or @postEditableProperties or [] + @jsonSchema = @modelClass?.jsonSchema or @jsonSchema or {} # subclasses should override these methods hasAccess: (req) -> true hasAccessToDocument: (req, document, method=null) -> return true if req.user?.isAdmin() - if @modelClass.schema.uses_coco_translation_coverage and (method or req.method).toLowerCase() is 'put' + if @modelClass.schema.uses_coco_translation_coverage and (method or req.method).toLowerCase() in ['post', 'put'] return true if @isJustFillingTranslations(req, document) if @modelClass.schema.uses_coco_permissions @@ -53,7 +53,7 @@ module.exports = class Handler return false unless delta.o.length is 1 index = delta.deltaPath.indexOf('i18n') return false if index is -1 - return false if delta.deltaPath[index+1] is 'en-US' + return false if delta.deltaPath[index+1] in ['en', 'en-US', 'en-GB'] # English speakers are most likely just spamming, so always treat those as patches, not saves. return true formatEntity: (req, document) -> document?.toObject() @@ -79,23 +79,24 @@ module.exports = class Handler sendNotFoundError: (res, message) -> errors.notFound(res, message) sendMethodNotAllowed: (res, message) -> errors.badMethod(res, @allowedMethods, message) sendBadInputError: (res, message) -> errors.badInput(res, message) + sendPaymentRequiredError: (res, message) -> errors.paymentRequired(res, message) sendDatabaseError: (res, err) -> - return @sendError(res, err.code, err.response) if err.response and err.code + return @sendError(res, err.code, err.response) if err?.response and err?.code log.error "Database error, #{err}" errors.serverError(res, 'Database error, ' + err) sendError: (res, code, message) -> errors.custom(res, code, message) - sendSuccess: (res, message) -> - res.send(message) + sendSuccess: (res, message='{}') -> + res.send 200, message res.end() - sendCreated: (res, message) -> + sendCreated: (res, message='{}') -> res.send 201, message res.end() - sendAccepted: (res, message) -> + sendAccepted: (res, message='{}') -> res.send 202, message res.end() @@ -105,7 +106,7 @@ module.exports = class Handler # generic handlers get: (req, res) -> - @sendForbiddenError(res) if not @hasAccess(req) + return @sendForbiddenError(res) if not @hasAccess(req) specialParameters = ['term', 'project', 'conditions'] @@ -163,19 +164,17 @@ module.exports = class Handler res.send matchedObjects res.end() if term - filter.project = projection - @modelClass.textSearch term, filter, callback + filter.filter.$text = $search: term + args = [filter.filter] + args.push projection if projection + q = @modelClass.find(args...) + if skip? and skip < 1000000 + q.skip(skip) + if limit? and limit < FETCH_LIMIT + q.limit(limit) else - args = [filter.filter] - args.push projection if projection - q = @modelClass.find(args...) - if skip? and skip < 1000000 - q.skip(skip) - if limit? and limit < FETCH_LIMIT - q.limit(limit) - else - q.limit(FETCH_LIMIT) - q.exec callback + q.limit(FETCH_LIMIT) + q.exec callback # if it's not a text search but the user is an admin, let him try stuff anyway else if req.user?.isAdmin() # admins can send any sort of query down the wire @@ -186,14 +185,17 @@ module.exports = class Handler # Conditions are chained query functions, for example: query.find().limit(20).sort('-dateCreated') conditions = JSON.parse(req.query.conditions || '[]') + hasLimit = false try for condition in conditions name = condition[0] f = query[name] args = condition[1..] query = query[name](args...) + hasLimit ||= f is 'limit' catch e return @sendError(res, 422, 'Badly formed conditions.') + query.limit(2000) unless hasLimit query.exec (err, documents) => return @sendDatabaseError(res, err) if err @@ -204,13 +206,15 @@ module.exports = class Handler return @sendForbiddenError(res) getById: (req, res, id) -> - # return @sendNotFoundError(res) # for testing return @sendForbiddenError(res) unless @hasAccess(req) - - @getDocumentForIdOrSlug id, (err, document) => + if req.query.project + projection = {} + projection[field] = 1 for field in req.query.project.split(',') + @getDocumentForIdOrSlug id, projection, (err, document) => return @sendDatabaseError(res, err) if err return @sendNotFoundError(res) unless document? return @sendForbiddenError(res) unless @hasAccessToDocument(req, document) + res.setHeader 'Cache-Control', 'no-cache' unless Handler.isID(id + '') # Don't cache if it's a slug instead of an ID @sendSuccess(res, @formatEntity(req, document)) getByRelationship: (req, res, args...) -> @@ -226,19 +230,20 @@ module.exports = class Handler return @getNamesByOriginals(req, res) @getPropertiesFromMultipleDocuments res, User, 'name', ids - getNamesByOriginals: (req, res) -> + getNamesByOriginals: (req, res, nonVersioned=false) -> ids = req.query.ids or req.body.ids ids = ids.split(',') if _.isString ids ids = _.uniq ids - # HACK: levels loading thang types need the components returned as well + # Hack: levels loading thang types need the components returned as well. # Need a way to specify a projection for a query. - project = {name:1, original:1, kind:1, components: 1} - sort = {'version.major':-1, 'version.minor':-1} + project = {name: 1, original: 1, kind: 1, components: 1} + sort = if nonVersioned then {} else {'version.major': -1, 'version.minor': -1} makeFunc = (id) => (callback) => - criteria = {original:mongoose.Types.ObjectId(id)} + criteria = {} + criteria[if nonVersioned then '_id' else 'original'] = mongoose.Types.ObjectId(id) @modelClass.findOne(criteria, project).sort(sort).exec (err, document) -> return done(err) if err callback(null, document?.toObject() or null) @@ -254,7 +259,12 @@ module.exports = class Handler res.end() getPatchesFor: (req, res, id) -> - query = { 'target.original': mongoose.Types.ObjectId(id), status: req.query.status or 'pending' } + query = + $or: [ + {'target.original': id+''} + {'target.original': mongoose.Types.ObjectId(id)} + ] + status: req.query.status or 'pending' Patch.find(query).sort('-created').exec (err, patches) => return @sendDatabaseError(res, err) if err patches = (patch.toObject() for patch in patches) @@ -298,6 +308,8 @@ module.exports = class Handler getLatestVersion: (req, res, original, version) -> # can get latest overall version, latest of a major version, or a specific version + return @sendBadInputError(res, 'Invalid MongoDB id: '+original) if not Handler.isID(original) + query = { 'original': mongoose.Types.ObjectId(original) } if version? version = version.split('.') @@ -311,10 +323,17 @@ module.exports = class Handler projection = {} fields = if req.query.project is 'true' then _.keys(PROJECT) else req.query.project.split(',') projection[field] = 1 for field in fields + # Make sure that permissions and version are fetched, but not sent back if they didn't ask for them. + extraProjectionProps = [] + extraProjectionProps.push 'permissions' unless projection.permissions + extraProjectionProps.push 'version' unless projection.version + projection.permissions = 1 + projection.version = 1 args.push projection @modelClass.findOne(args...).sort(sort).exec (err, doc) => return @sendNotFoundError(res) unless doc? return @sendForbiddenError(res) unless @hasAccessToDocument(req, doc) + doc = _.omit doc, extraProjectionProps if extraProjectionProps? res.send(doc) res.end() @@ -325,7 +344,7 @@ module.exports = class Handler put: (req, res, id) -> # Client expects PATCH behavior for PUTs # Real PATCHs return incorrect HTTP responses in some environments (e.g. Browserstack, schools) - return @postNewVersion(req, res) if @modelClass.schema.uses_coco_versions + return @sendForbiddenError(res) if @modelClass.schema.uses_coco_versions and not req.user.isAdmin() # Campaign editor just saves over things. return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body) return @sendForbiddenError(res) unless @hasAccess(req) @getDocumentForIdOrSlug req.body._id or id, (err, document) => @@ -334,11 +353,13 @@ module.exports = class Handler return @sendNotFoundError(res) unless document? return @sendForbiddenError(res) unless @hasAccessToDocument(req, document) @doWaterfallChecks req, document, (err, document) => + return if err is true return @sendError(res, err.code, err.res) if err @saveChangesToDocument req, document, (err) => return @sendBadInputError(res, err.errors) if err?.valid is false return @sendDatabaseError(res, err) if err @sendSuccess(res, @formatEntity(req, document)) + @onPutSuccess(req, document) post: (req, res) -> if @modelClass.schema.uses_coco_versions @@ -358,6 +379,7 @@ module.exports = class Handler @onPostSuccess(req, document) onPostSuccess: (req, doc) -> + onPutSuccess: (req, doc) -> ### TODO: think about pulling some common stuff out of postFirstVersion/postNewVersion @@ -428,7 +450,8 @@ module.exports = class Handler docLink = "http://codecombat.com#{editPath}" @sendChangedHipChatMessage creator: editor, target: changedDocument, docLink: docLink watchers = changedDocument.get('watchers') or [] - watchers = (w for w in watchers when not w.equals(editor.get('_id'))) + # Don't send these emails to the person who submitted the patch, or to Nick, George, or Scott. + watchers = (w for w in watchers when not w.equals(editor.get('_id')) and not (w + '' in ['512ef4805a67a8c507000001', '5162fab9c92b4c751e000274', '51538fdb812dd9af02000001'])) return unless watchers.length User.find({_id:{$in:watchers}}).select({email:1, name:1}).exec (err, watchers) => for watcher in watchers @@ -448,8 +471,9 @@ module.exports = class Handler sendwithus.api.send context, (err, result) -> sendChangedHipChatMessage: (options) -> - message = "#{options.creator.get('name')} saved a change to <a href=\"#{options.docLink}\">#{options.target.get('name')}</a>: #{options.target.get('commitMessage')}" - hipchat.sendHipChatMessage message + message = "#{options.creator.get('name')} saved a change to <a href=\"#{options.docLink}\">#{options.target.get('name')}</a>: #{options.target.get('commitMessage') or '(no commit message)'}" + rooms = if /Diplomat submission/.test(message) then ['main'] else ['main', 'artisans'] + hipchat.sendHipChatMessage message, rooms makeNewInstance: (req) -> model = new @modelClass({}) @@ -457,9 +481,7 @@ module.exports = class Handler watchers = [req.user.get('_id')] if req.user.isAdmin() # https://github.com/codecombat/codecombat/issues/1105 nick = mongoose.Types.ObjectId('512ef4805a67a8c507000001') - scott = mongoose.Types.ObjectId('5162fab9c92b4c751e000274') watchers.push nick unless _.find watchers, (id) -> id.equals nick - watchers.push scott unless _.find watchers, (id) -> id.equals scott model.set 'watchers', watchers model @@ -470,14 +492,18 @@ module.exports = class Handler @isID: (id) -> _.isString(id) and id.length is 24 and id.match(/[a-f0-9]/gi)?.length is 24 - getDocumentForIdOrSlug: (idOrSlug, done) -> + getDocumentForIdOrSlug: (idOrSlug, projection, done) -> + unless done + done = projection # projection is optional argument + projection = null idOrSlug = idOrSlug+'' if Handler.isID(idOrSlug) - @modelClass.findById(idOrSlug).exec (err, document) -> - done(err, document) + query = @modelClass.findById(idOrSlug) else - @modelClass.findOne {slug: idOrSlug}, (err, document) -> - done(err, document) + query = @modelClass.findOne {slug: idOrSlug} + query.select projection if projection + query.exec (err, document) -> + done(err, document) doWaterfallChecks: (req, document, done) -> return done(null, document) unless @waterfallFunctions.length diff --git a/server/commons/database.coffee b/server/commons/database.coffee index c0656562c..18e8248fe 100644 --- a/server/commons/database.coffee +++ b/server/commons/database.coffee @@ -2,8 +2,9 @@ config = require '../../server_config' winston = require 'winston' mongoose = require 'mongoose' Grid = require 'gridfs-stream' +mongooseCache = require 'mongoose-cache' -testing = '--unittest' in process.argv +global.testing = testing = '--unittest' in process.argv module.exports.connect = () -> @@ -13,9 +14,16 @@ module.exports.connect = () -> mongoose.connect address mongoose.connection.once 'open', -> Grid.gfs = Grid(mongoose.connection.db, mongoose.mongo) + # Hack around Mongoose not exporting Aggregate so that we can patch its exec, too + # https://github.com/LearnBoost/mongoose/issues/1910 + Level = require '../levels/Level' + Aggregate = Level.aggregate().constructor + mongooseCache.install(mongoose, {max: 200, maxAge: 1 * 60 * 1000, debug: false}, Aggregate) module.exports.generateMongoConnectionString = -> - if not testing and config.mongo.mongoose_replica_string + if not testing and config.tokyo + address = config.mongo.mongoose_tokyo_replica_string + else if not testing and config.mongo.mongoose_replica_string address = config.mongo.mongoose_replica_string else dbName = config.mongo.db diff --git a/server/commons/errors.coffee b/server/commons/errors.coffee index 3648cc0b9..7f0361d0d 100644 --- a/server/commons/errors.coffee +++ b/server/commons/errors.coffee @@ -15,6 +15,11 @@ module.exports.forbidden = (res, message='Forbidden') -> log.debug "403: #{message}" res.send 403, message res.end() + +module.exports.paymentRequired = (res, message='Payment required') -> + log.debug "402: #{message}" + res.send 402, message + res.end() module.exports.notFound = (res, message='Not found.') -> res.send 404, message diff --git a/server/commons/mail.coffee b/server/commons/mail.coffee index e270bb2cd..432441060 100644 --- a/server/commons/mail.coffee +++ b/server/commons/mail.coffee @@ -6,10 +6,3 @@ module.exports.MAILCHIMP_GROUP_ID = '4529' # these two need to be parallel module.exports.MAILCHIMP_GROUPS = ['Announcements', 'Adventurers', 'Artisans', 'Archmages', 'Scribes', 'Diplomats', 'Ambassadors'] module.exports.NEWS_GROUPS = ['generalNews', 'adventurerNews', 'artisanNews', 'archmageNews', 'scribeNews', 'diplomatNews', 'ambassadorNews'] - -nodemailer = require 'nodemailer' -module.exports.transport = nodemailer.createTransport 'SMTP', - service: config.mail.service - user: config.mail.username - pass: config.mail.password - authMethod: 'LOGIN' diff --git a/server/commons/mapping.coffee b/server/commons/mapping.coffee index c6393a39b..5b3ae2970 100644 --- a/server/commons/mapping.coffee +++ b/server/commons/mapping.coffee @@ -1,5 +1,13 @@ module.exports.handlers = + 'analytics_log_event': 'analytics/analytics_log_event_handler' + 'analytics_perday': 'analytics/analytics_perday_handler' + 'analytics_string': 'analytics/analytics_string_handler' + 'analytics_stripe_invoice': 'analytics/analytics_stripe_invoice_handler' + # TODO: Disabling this until we know why our app servers CPU grows out of control. + # 'analytics_users_active': 'analytics/analytics_users_active_handler' 'article': 'articles/article_handler' + 'campaign': 'campaigns/campaign_handler' + 'clan': 'clans/clan_handler' 'level': 'levels/level_handler' 'level_component': 'levels/components/level_component_handler' 'level_feedback': 'levels/feedbacks/level_feedback_handler' @@ -15,6 +23,11 @@ module.exports.handlers = 'mail_sent': 'mail/sent/mail_sent_handler' 'achievement': 'achievements/achievement_handler' 'earned_achievement': 'achievements/earned_achievement_handler' + 'poll': 'polls/poll_handler' + 'prepaid': 'prepaids/prepaid_handler' + 'subscription': 'payments/subscription_handler' + 'trial_request': 'trial_requests/trial_request_handler' + 'user_polls_record': 'polls/user_polls_record_handler' module.exports.routes = [ @@ -30,6 +43,7 @@ module.exports.routes = 'routes/sprites' 'routes/queue' 'routes/stacklead' + 'routes/stripe' ] mongoose = require 'mongoose' diff --git a/server/hipchat.coffee b/server/hipchat.coffee index e8b72fca2..0d872c2d3 100644 --- a/server/hipchat.coffee +++ b/server/hipchat.coffee @@ -2,32 +2,34 @@ config = require '../server_config' request = require 'request' log = require 'winston' -module.exports.sendHipChatMessage = sendHipChatMessage = (message) -> - return unless key = config.hipchatAPIKey - return unless config.isProduction - roomID = 254598 - form = - color: 'yellow' - notify: false - message: message - messageFormat: 'html' - url = "https://api.hipchat.com/v2/room/#{roomID}/notification?auth_token=#{key}" - request.post {uri: url, json: form}, (err, res, body) -> - return log.error 'Error sending HipChat patch request:', err or body if err or /error/i.test body - #log.info "Got HipChat patch response:", body +roomIDMap = + main: 254598 + artisans: 1146994 + tower: 318356 -module.exports.sendTowerHipChatMessage = sendTowerHipChatMessage = (message) -> - secondsFromEpoch = Math.floor(new Date().getTime() / 1000) - link = "<a href=\"https://papertrailapp.com/groups/488214/events?time=#{secondsFromEpoch}\">PaperTrail</a>" - message = "#{message} #{link}" - return unless key = config.hipchatTowerAPIKey +module.exports.sendHipChatMessage = sendHipChatMessage = (message, rooms, options) -> return unless config.isProduction - roomID = 318356 - form = - color: 'red' - notify: true - message: message - messageFormat: 'html' - url = "https://api.hipchat.com/v2/room/#{roomID}/notification?auth_token=#{key}" - request.post {uri: url, json: form}, (err, res, body) -> - return log.error 'Error sending HipChat Tower message:', err or body if err or /error/i.test body + rooms ?= ['main'] + options ?= {} + for room in rooms + unless roomID = roomIDMap[room] + log.error "Unknown HipChat room #{room}." + continue + unless key = config.hipchat[room] + log.info "No HipChat API key for room #{room}." + continue + form = + color: options.color or 'yellow' + notify: false + message: message + messageFormat: 'html' + if options.papertrail + secondsFromEpoch = Math.floor(new Date().getTime() / 1000) + link = "<a href=\"https://papertrailapp.com/groups/488214/events?time=#{secondsFromEpoch}\">PaperTrail</a>" + form.message = "#{message} #{link}" + form.color = options.color or 'red' + form.notify = true + url = "https://api.hipchat.com/v2/room/#{roomID}/notification?auth_token=#{key}" + request.post {uri: url, json: form}, (err, res, body) -> + return log.error 'Error sending HipChat message:', err or body if err or /error/i.test body + #log.info "Got HipChat message response:", body diff --git a/server/levels/Level.coffee b/server/levels/Level.coffee index 95d2bc4b7..5eef1b747 100644 --- a/server/levels/Level.coffee +++ b/server/levels/Level.coffee @@ -1,10 +1,37 @@ mongoose = require 'mongoose' plugins = require '../plugins/plugins' jsonschema = require '../../app/schemas/models/level' +config = require '../../server_config' LevelSchema = new mongoose.Schema({ description: String -}, {strict: false}) +}, {strict: false, read:config.mongo.readpref}) + +LevelSchema.index( + { + index: 1 + _fts: 'text' + _ftsx: 1 + }, + { + name: 'search index' + sparse: true + weights: {description: 1, name: 1} + default_language: 'english' + 'language_override': 'searchLanguage' + 'textIndexVersion': 2 + }) +LevelSchema.index( + { + original: 1 + 'version.major': -1 + 'version.minor': -1 + }, + { + name: 'version index' + unique: true + }) +LevelSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true}) LevelSchema.plugin(plugins.NamedPlugin) LevelSchema.plugin(plugins.PermissionsPlugin) diff --git a/server/levels/components/LevelComponent.coffee b/server/levels/components/LevelComponent.coffee index a714129c0..68280f661 100644 --- a/server/levels/components/LevelComponent.coffee +++ b/server/levels/components/LevelComponent.coffee @@ -1,11 +1,38 @@ mongoose = require 'mongoose' plugins = require '../../plugins/plugins' jsonschema = require '../../../app/schemas/models/level_component' +config = require '../../../server_config' LevelComponentSchema = new mongoose.Schema { description: String system: String -}, {strict: false} +}, {strict: false, read:config.mongo.readpref} + +LevelComponentSchema.index( + { + index: 1 + _fts: 'text' + _ftsx: 1 + }, + { + name: 'search index' + sparse: true + weights: {description: 1, name: 1, searchStrings: 1} + default_language: 'english' + 'language_override': 'searchLanguage' + 'textIndexVersion': 2 + }) +LevelComponentSchema.index( + { + original: 1 + 'version.major': -1 + 'version.minor': -1 + }, + { + name: 'version index' + unique: true + }) +LevelComponentSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true}) LevelComponentSchema.plugin plugins.NamedPlugin LevelComponentSchema.plugin plugins.PermissionsPlugin diff --git a/server/levels/components/level_component_handler.coffee b/server/levels/components/level_component_handler.coffee index cf40eff50..67997abe2 100644 --- a/server/levels/components/level_component_handler.coffee +++ b/server/levels/components/level_component_handler.coffee @@ -23,6 +23,9 @@ LevelComponentHandler = class LevelComponentHandler extends Handler props.push('official') if req.user?.isAdmin() props + hasAccessToDocument: (req, document, method) -> + if req.user?.isArtisan() then true else super req, document, method + get: (req, res) -> if req.query.view is 'prop-doc-lookup' projection = {} diff --git a/server/levels/feedbacks/LevelFeedback.coffee b/server/levels/feedbacks/LevelFeedback.coffee index 523bf9120..1e3a47950 100644 --- a/server/levels/feedbacks/LevelFeedback.coffee +++ b/server/levels/feedbacks/LevelFeedback.coffee @@ -3,11 +3,15 @@ mongoose = require 'mongoose' plugins = require '../../plugins/plugins' jsonschema = require '../../../app/schemas/models/level_feedback' +config = require '../../../server_config' LevelFeedbackSchema = new mongoose.Schema({ created: type: Date 'default': Date.now -}, {strict: false}) +}, {strict: false,read:config.mongo.readpref}) + +LevelFeedbackSchema.index({created: 1}) +LevelFeedbackSchema.index({creator: 1}) module.exports = LevelFeedback = mongoose.model('level.feedback', LevelFeedbackSchema) diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee index fcb311dea..26ea7ac26 100644 --- a/server/levels/level_handler.coffee +++ b/server/levels/level_handler.coffee @@ -6,6 +6,8 @@ Feedback = require './feedbacks/LevelFeedback' Handler = require '../commons/Handler' mongoose = require 'mongoose' async = require 'async' +utils = require '../lib/utils' +log = require 'winston' LevelHandler = class LevelHandler extends Handler modelClass: Level @@ -30,6 +32,35 @@ LevelHandler = class LevelHandler extends Handler 'terrain' 'i18nCoverage' 'loadingTip' + 'requiresSubscription' + 'adventurer' + 'practice' + 'adminOnly' + 'disableSpaces' + 'hidesSubmitUntilRun' + 'hidesPlayButton' + 'hidesRunShortcut' + 'hidesHUD' + 'hidesSay' + 'hidesCodeToolbar' + 'hidesRealTimePlayback' + 'backspaceThrottle' + 'lockDefaultCode' + 'moveRightLoopSnippet' + 'realTimeSpeedFactor' + 'autocompleteFontSizePx' + 'requiredCode' + 'suspectCode' + 'requiredGear' + 'restrictedGear' + 'allowedHeroes' + 'tasks' + 'helpVideos' + 'campaign' + 'replayable' + 'buildTime' + 'scoreTypes' + 'concepts' ] postEditableProperties: ['name'] @@ -47,9 +78,12 @@ LevelHandler = class LevelHandler extends Handler return @getHistogramData(req, res, args[0]) if args[1] is 'histogram_data' return @checkExistence(req, res, args[0]) if args[1] is 'exists' return @getPlayCountsBySlugs(req, res) if args[1] is 'play_counts' + return @getLevelPlaytimesBySlugs(req, res) if args[1] is 'playtime_averages' + return @getTopScores(req, res, args[0], args[2], args[3]) if args[1] is 'top_scores' super(arguments...) fetchLevelByIDAndHandleErrors: (id, req, res, callback) -> + # TODO: this could probably be faster with projections, right? @getDocumentForIdOrSlug id, (err, level) => return @sendDatabaseError(res, err) if err return @sendNotFoundError(res) unless level? @@ -71,6 +105,9 @@ LevelHandler = class LevelHandler extends Handler Session.findOne(sessionQuery).exec (err, doc) => return @sendDatabaseError(res, err) if err return @sendSuccess(res, doc) if doc? + requiresSubscription = level.get('requiresSubscription') or (req.user.get('chinaVersion') and level.get('campaign') and not (level.slug in ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'forgetful-gemsmith', 'signs-and-portents', 'true-names'])) + canPlayAnyway = req.user.isPremium() or level.get 'adventurer' + return @sendPaymentRequiredError(res, err) if requiresSubscription and not canPlayAnyway @createAndSaveNewSession sessionQuery, req, res createAndSaveNewSession: (sessionQuery, req, res) => @@ -122,17 +159,19 @@ LevelHandler = class LevelHandler extends Handler majorVersion: level.version.major creator: req.user._id+'' - query = Session.find(sessionQuery).select('-screenshot') + query = Session.find(sessionQuery).select('-screenshot -transpiledCode') + # TODO: take out "code" as well, since that can get huge containing the transpiled code for the lat hero, and find another way of having the LadderSubmissionViews in the MyMatchesTab determine rankin readiness query.exec (err, results) => if err then @sendDatabaseError(res, err) else @sendSuccess res, results getHistogramData: (req, res, slug) -> - query = Session.aggregate [ + aggregate = Session.aggregate [ {$match: {'levelID': slug, 'submitted': true, 'team': req.query.team}} {$project: {totalScore: 1, _id: 0}} ] + aggregate.cache() - query.exec (err, data) => + aggregate.exec (err, data) => if err? then return @sendDatabaseError res, err valueArray = _.pluck data, 'totalScore' @sendSuccess res, valueArray @@ -159,13 +198,14 @@ LevelHandler = class LevelHandler extends Handler sortParameters = 'totalScore': req.query.order - selectProperties = ['totalScore', 'creatorName', 'creator', 'submittedCodeLanguage'] + selectProperties = ['totalScore', 'creatorName', 'creator', 'submittedCodeLanguage', 'heroConfig'] query = Session .find(sessionsQueryParameters) .limit(req.query.limit) .sort(sortParameters) .select(selectProperties.join ' ') + query.cache() if sessionsQueryParameters.totalScore.$lt is 1000000 query.exec (err, resultSessions) => return @sendDatabaseError(res, err) if err @@ -236,6 +276,7 @@ LevelHandler = class LevelHandler extends Handler query = Level.findOne(findParameters) .select(selectString) .lean() + .cache() query.exec (err, level) => return @sendDatabaseError(res, err) if err @@ -247,17 +288,19 @@ LevelHandler = class LevelHandler extends Handler majorVersion: level.version.major submitted: true - query = Session.find(sessionsQueryParameters).distinct('team') + query = Session.find(sessionsQueryParameters).distinct('team').cache() query.exec (err, teams) => return @sendDatabaseError res, err if err? or not teams findTop20Players = (sessionQueryParams, team, cb) -> sessionQueryParams['team'] = team - Session.aggregate [ + aggregate = Session.aggregate [ {$match: sessionQueryParams} - {$project: {'totalScore': 1}} {$sort: {'totalScore': -1}} {$limit: 20} - ], cb + {$project: {'totalScore': 1}} + ] + aggregate.cache() + aggregate.exec cb async.map teams, findTop20Players.bind(@, sessionsQueryParameters), (err, map) => if err? then return @sendDatabaseError(res, err) @@ -291,11 +334,13 @@ LevelHandler = class LevelHandler extends Handler # This is hella slow (4s on my box), so relying on some dumb caching for it. # If we can't make this faster with indexing or something, we might want to maintain the counts another way. levelIDs = req.query.ids or req.body.ids + return @sendSuccess res, [] unless levelIDs? + @playCountCache ?= {} @playCountCachedSince ?= new Date() if (new Date()) - @playCountCachedSince > 86400 * 1000 # Dumb cache expiration @playCountCache = {} - @playCountCacheSince = new Date() + @playCountCachedSince = new Date() cacheKey = levelIDs.join ',' if playCounts = @playCountCache[cacheKey] return @sendSuccess res, playCounts @@ -310,8 +355,90 @@ LevelHandler = class LevelHandler extends Handler @sendSuccess res, data hasAccessToDocument: (req, document, method=null) -> + return true if req.user?.isArtisan() method ?= req.method return true if method is null or method is 'get' super(req, document, method) + getLevelPlaytimesBySlugs: (req, res) -> + # Returns an array of per-day level average playtimes + # Parameters: + # slugs - array of level slugs + # startDay - Inclusive, optional, e.g. '2014-12-14' + # endDay - Exclusive, optional, e.g. '2014-12-16' + + # TODO: An uncached call takes about 5s for dungeons-of-kithgard locally + # TODO: This is very similar to getLevelCompletionsBySlugs(), time to generalize analytics APIs? + + # TODO: exclude admin data + + levelSlugs = req.query.slugs or req.body.slugs + startDay = req.query.startDay or req.body.startDay + endDay = req.query.endDay or req.body.endDay + + return @sendSuccess res, [] unless levelSlugs? + + # log.warn "playtime_averages levelSlugs='#{levelSlugs}' startDay=#{startDay} endDay=#{endDay}" + + # Cache results for 1 day + @levelPlaytimesCache ?= {} + @levelPlaytimesCachedSince ?= new Date() + if (new Date()) - @levelPlaytimesCachedSince > 86400 * 1000 # Dumb cache expiration + @levelPlaytimesCache = {} + @levelPlaytimesCachedSince = new Date() + cacheKey = levelSlugs.join(',') + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + return @sendSuccess res, levelPlaytimes if levelPlaytimes = @levelPlaytimesCache[cacheKey] + + # Build query + match = {$match: {$and: [{"state.complete": true}, {"playtime": {$gt: 0}}, {levelID: {$in: levelSlugs}}]}} + match["$match"]["$and"].push _id: {$gte: utils.objectIdFromTimestamp(startDay + "T00:00:00.000Z")} if startDay? + match["$match"]["$and"].push _id: {$lt: utils.objectIdFromTimestamp(endDay + "T00:00:00.000Z")} if endDay? + project = {"$project": {"_id": 0, "levelID": 1, "playtime": 1, "created": {"$concat": [{"$substr": ["$created", 0, 10]}]}}} + group = {"$group": {"_id": {"created": "$created", "level": "$levelID"}, "average": {"$avg": "$playtime"}}} + query = Session.aggregate match, project, group + + query.exec (err, data) => + if err? then return @sendDatabaseError res, err + + # Build list of level average playtimes + playtimes = [] + for item in data + playtimes.push + level: item._id.level + created: item._id.created + average: item.average + @levelPlaytimesCache[cacheKey] = playtimes + @sendSuccess res, playtimes + + getTopScores: (req, res, levelOriginal, scoreType, timespan) -> + query = + 'level.original': levelOriginal + 'state.topScores.type': scoreType + now = new Date() + if timespan is 'day' + since = new Date now - 1 * 86400 * 1000 + else if timespan is 'week' + since = new Date now - 7 * 86400 * 1000 + if since + query['state.topScores.date'] = $gt: since.toISOString() + + sort = + 'state.topScores.score': -1 + + select = ['state.topScores', 'creatorName', 'creator', 'codeLanguage', 'heroConfig'] + + query = Session + .find(query) + .limit(20) + .sort(sort) + .select(select.join ' ') + + query.exec (err, resultSessions) => + return @sendDatabaseError(res, err) if err + resultSessions ?= [] + @sendSuccess res, resultSessions + + module.exports = new LevelHandler() diff --git a/server/levels/sessions/LevelSession.coffee b/server/levels/sessions/LevelSession.coffee index 46beecef2..f2060ab63 100644 --- a/server/levels/sessions/LevelSession.coffee +++ b/server/levels/sessions/LevelSession.coffee @@ -1,47 +1,78 @@ -# TODO: not updated since rename from level_instance, or since we redid how all models are done; probably busted - mongoose = require 'mongoose' plugins = require '../../plugins/plugins' AchievablePlugin = require '../../plugins/achievements' jsonschema = require '../../../app/schemas/models/level_session' log = require 'winston' +config = require '../../../server_config' LevelSessionSchema = new mongoose.Schema({ created: type: Date 'default': Date.now -}, {strict: false}) +}, {strict: false,read:config.mongo.readpref}) + +LevelSessionSchema.index({creator: 1}) +LevelSessionSchema.index({level: 1}) +LevelSessionSchema.index({levelID: 1}) +LevelSessionSchema.index({'level.majorVersion': 1}) +LevelSessionSchema.index({'level.original': 1}, {name: 'Level Original'}) +LevelSessionSchema.index({'level.original': 1, 'level.majorVersion': 1, 'creator': 1, 'team': 1}) +LevelSessionSchema.index({playtime: 1}, {name: 'Playtime'}) +LevelSessionSchema.index({submitDate: 1}) +LevelSessionSchema.index({submitted: 1}, {sparse: true}) +LevelSessionSchema.index({team: 1}, {sparse: true}) +LevelSessionSchema.index({totalScore: 1}, {sparse: true}) +LevelSessionSchema.index({user: 1, changed: -1}, {name: 'last played index', sparse: true}) +LevelSessionSchema.index({'level.original': 1, 'state.topScores.type': 1, 'state.topScores.date': -1, 'state.topScores.score': -1}, {name: 'top scores index', sparse: true}) +LevelSessionSchema.index({submitted: 1, team: 1, level:1, totalScore: -1}, {name: 'rank counting index', sparse: true}) +LevelSessionSchema.index({levelID: 1, submitted:1, team: 1}, {name: 'get all scores index', sparse: true}) +LevelSessionSchema.index({submitted: 1, team: 1, levelID: 1, submitDate: -1}, {name: 'matchmaking index', sparse: true}) +LevelSessionSchema.index({submitted: 1, team: 1, levelID: 1, randomSimulationIndex: -1}, {name: 'matchmaking random index', sparse: true}) + LevelSessionSchema.plugin(plugins.PermissionsPlugin) LevelSessionSchema.plugin(AchievablePlugin) -previous = {} - LevelSessionSchema.post 'init', (doc) -> - previous[doc.get 'id'] = - 'state.completed': doc.get 'state.completed' + unless doc.previousStateInfo + doc.previousStateInfo = + 'state.complete': doc.get 'state.complete' + playtime: doc.get 'playtime' LevelSessionSchema.pre 'save', (next) -> + User = require '../../users/User' # Avoid mutual inclusion cycles @set('changed', new Date()) id = @get('id') - initd = id of previous + initd = @previousStateInfo? + levelID = @get('levelID') + userID = @get('creator') + activeUserEvent = null - # newly completed level - if not (initd and previous[id]['state.completed']) and @get('state.completed') - User = require '../../users/User' # Avoid mutual inclusion cycles - User.update {_id: @get 'creator'}, {$inc: 'stats.gamesCompleted': 1}, {}, (err, count) -> + # Newly completed level + if not (initd and @previousStateInfo['state.complete']) and @get('state.complete') + User.findByIdAndUpdate userID, {$inc: 'stats.gamesCompleted': 1}, {}, (err, doc) -> log.error err if err? + oldCopy = doc.toObject() + oldCopy.stats = _.clone oldCopy.stats + oldCopy.stats.gamesCompleted = oldCopy.stats.gamesCompleted - 1 + User.schema.statics.createNewEarnedAchievements doc, oldCopy + activeUserEvent = "level-completed/#{levelID}" - delete previous[id] if initd - next() + # Spent at least 30s playing this level + if not initd and @get('playtime') >= 30 or initd and (@get('playtime') - @previousStateInfo['playtime'] >= 30) + activeUserEvent = "level-playtime/#{levelID}" + + if activeUserEvent? + User.saveActiveUser userID, activeUserEvent, next + else + next() LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed'] LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state', 'levelName', 'creatorName', 'levelID', 'screenshot', - 'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', - 'unsubscribed', 'playtime', 'heroConfig', 'team', 'transpiledCode'] + 'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', + 'unsubscribed', 'playtime', 'heroConfig', 'team', 'transpiledCode', + 'browser'] LevelSessionSchema.statics.jsonSchema = jsonschema -LevelSessionSchema.index {user: 1, changed: -1}, {sparse: true, name: 'last played index'} - module.exports = LevelSession = mongoose.model('level.session', LevelSessionSchema, 'level.sessions') diff --git a/server/levels/sessions/level_session_handler.coffee b/server/levels/sessions/level_session_handler.coffee index 62637c177..5e4780d89 100644 --- a/server/levels/sessions/level_session_handler.coffee +++ b/server/levels/sessions/level_session_handler.coffee @@ -9,12 +9,16 @@ class LevelSessionHandler extends Handler getByRelationship: (req, res, args...) -> return @getActiveSessions req, res if args.length is 2 and args[1] is 'active' + return @getRecentSessions req, res if args.length is 2 and args[1] is 'recent' return @getCodeLanguageCounts req, res if args[1] is 'code_language_counts' super(arguments...) formatEntity: (req, document) -> documentObject = super(req, document) - if req.user?.isAdmin() or req.user?.id is document.creator or ('employer' in (req.user?.get('permissions') ? [])) + if req.user?.isAdmin() or + req.user?.id is document.creator or + ('employer' in (req.user?.get('permissions') ? [])) or + not document.submittedCode # TODO: only allow leaderboard access to non-top-5 solutions return documentObject else return _.omit documentObject, @privateProperties @@ -29,9 +33,27 @@ class LevelSessionHandler extends Handler documents = (@formatEntity(req, doc) for doc in documents) @sendSuccess(res, documents) + getRecentSessions: (req, res) -> + return @sendForbiddenError(res) unless req.user?.isAdmin() + + levelSlug = req.query.slug or req.body.slug + limit = req.query.limit or req.body.limit or 7 + + return @sendSuccess res, [] unless levelSlug? + + today = new Date() + today.setUTCMinutes(today.getUTCMinutes() - 10) + queryParams = {$and: [{"changed": {"$lt": today}}, {"levelID": levelSlug}]} + query = @modelClass.find(queryParams).sort({changed: -1}).limit(limit) + query.exec (err, documents) => + return @sendDatabaseError(res, err) if err + @sendSuccess res, documents + hasAccessToDocument: (req, document, method=null) -> - return true if req.method is 'GET' and document.get('submitted') - return true if ('employer' in (req.user?.get('permissions') ? [])) and (method ? req.method).toLowerCase() is 'get' + get = (method ? req.method).toLowerCase() is 'get' + return true if get and document.get('submitted') + return true if get and ('employer' in (req.user?.get('permissions') ? [])) + return true if get and not document.get('submittedCode') # Allow leaderboard access to non-multiplayer sessions super(arguments...) getCodeLanguageCounts: (req, res) -> diff --git a/server/levels/systems/LevelSystem.coffee b/server/levels/systems/LevelSystem.coffee index 87c4a2bab..75398fb4b 100644 --- a/server/levels/systems/LevelSystem.coffee +++ b/server/levels/systems/LevelSystem.coffee @@ -1,10 +1,37 @@ mongoose = require 'mongoose' plugins = require '../../plugins/plugins' jsonschema = require '../../../app/schemas/models/level_system' +config = require '../../../server_config' LevelSystemSchema = new mongoose.Schema { description: String -}, {strict: false} +}, {strict: false,read:config.mongo.readpref} + +LevelSystemSchema.index( + { + index: 1 + _fts: 'text' + _ftsx: 1 + }, + { + name: 'search index' + sparse: true + weights: {description: 1, name: 1, name: 1} + default_language: 'english' + 'language_override': 'searchLanguage' + 'textIndexVersion': 2 + }) +LevelSystemSchema.index( + { + original: 1 + 'version.major': -1 + 'version.minor': -1 + }, + { + name: 'version index' + unique: true + }) +LevelSystemSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true}) LevelSystemSchema.plugin(plugins.NamedPlugin) LevelSystemSchema.plugin(plugins.PermissionsPlugin) diff --git a/server/levels/systems/level_system_handler.coffee b/server/levels/systems/level_system_handler.coffee index ef9dd5d2b..4c356f1d7 100644 --- a/server/levels/systems/level_system_handler.coffee +++ b/server/levels/systems/level_system_handler.coffee @@ -21,7 +21,10 @@ LevelSystemHandler = class LevelSystemHandler extends Handler props hasAccess: (req) -> - req.method is 'GET' or req.user?.isAdmin() + req.method is 'GET' or req.user?.isAdmin() or req.user?.isArtisan() + + hasAccessToDocument: (req, document, method) -> + if req.user?.isArtisan() then true else super req, document, method module.exports = new LevelSystemHandler() diff --git a/server/levels/thangs/ThangType.coffee b/server/levels/thangs/ThangType.coffee index 02259caa3..732dc5f09 100644 --- a/server/levels/thangs/ThangType.coffee +++ b/server/levels/thangs/ThangType.coffee @@ -1,9 +1,36 @@ mongoose = require 'mongoose' plugins = require '../../plugins/plugins' +config = require '../../../server_config' ThangTypeSchema = new mongoose.Schema({ body: String, -}, {strict: false}) +}, {strict: false,read:config.mongo.readpref}) + +ThangTypeSchema.index( + { + index: 1 + _fts: 'text' + _ftsx: 1 + }, + { + name: 'search index' + sparse: true + weights: {name: 1} + default_language: 'english' + 'language_override': 'searchLanguage' + 'textIndexVersion': 2 + }) +ThangTypeSchema.index( + { + original: 1 + 'version.major': -1 + 'version.minor': -1 + }, + { + name: 'version index' + unique: true + }) +ThangTypeSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true}) ThangTypeSchema.plugin plugins.NamedPlugin ThangTypeSchema.plugin plugins.VersionedPlugin diff --git a/server/levels/thangs/thang_type_handler.coffee b/server/levels/thangs/thang_type_handler.coffee index e846816ac..6ca514c47 100644 --- a/server/levels/thangs/thang_type_handler.coffee +++ b/server/levels/thangs/thang_type_handler.coffee @@ -33,16 +33,18 @@ ThangTypeHandler = class ThangTypeHandler extends Handler 'tier' 'extendedName' 'unlockLevelName' + 'tasks' + 'terrains' ] hasAccess: (req) -> - req.method in ['GET', 'PUT'] or req.user?.isAdmin() + req.method in ['GET', 'POST'] or req.user?.isAdmin() hasAccessToDocument: (req, document, method=null) -> method = (method or req.method).toLowerCase() return true if method is 'get' - return true if req.user?.isAdmin() - return true if method is 'put' and @isJustFillingTranslations(req, document) + return true if req.user?.isAdmin() or req.user?.isArtisan() + return true if method is 'post' and @isJustFillingTranslations(req, document) return get: (req, res) -> @@ -68,6 +70,8 @@ ThangTypeHandler = class ThangTypeHandler extends Handler if limit? and limit < 1000 q.limit(limit) + q.cache() + q.exec (err, documents) => return @sendDatabaseError(res, err) if err documents = (@formatEntity(req, doc) for doc in documents) diff --git a/server/lib/utils.coffee b/server/lib/utils.coffee index d0fd9d09e..20474bc5f 100644 --- a/server/lib/utils.coffee +++ b/server/lib/utils.coffee @@ -1,3 +1,85 @@ +AnalyticsString = require '../analytics/AnalyticsString' +log = require 'winston' +mongoose = require 'mongoose' module.exports = isID: (id) -> _.isString(id) and id.length is 24 and id.match(/[a-f0-9]/gi)?.length is 24 + + objectIdFromTimestamp: (timestamp) -> + # mongoDB ObjectId contains creation date in first 4 bytes + # So, it can be used instead of a redundant created field + # http://docs.mongodb.org/manual/reference/object-id/ + # http://stackoverflow.com/questions/8749971/can-i-query-mongodb-objectid-by-date + # Convert string date to Date object (otherwise assume timestamp is a date) + timestamp = new Date(timestamp) if typeof(timestamp) == 'string' + # Convert date object to hex seconds since Unix epoch + hexSeconds = Math.floor(timestamp/1000).toString(16) + # Create an ObjectId with that hex timestamp + mongoose.Types.ObjectId(hexSeconds + "0000000000000000") + + findStripeSubscription: (customerID, options, done) -> + # Grabs latest subscription (e.g. in case of a resubscribe) + return done() unless customerID? + return done() unless options.subscriptionID? or options.userID? + subscriptionID = options.subscriptionID + userID = options.userID + + subscription = null + nextBatch = (starting_after, done) -> + options = limit: 100 + options.starting_after = starting_after if starting_after + stripe.customers.listSubscriptions customerID, options, (err, subscriptions) -> + return done(subscription) if err + return done(subscription) unless subscriptions?.data?.length > 0 + for sub in subscriptions.data + if subscriptionID? and sub.id is subscriptionID + unless subscription?.cancel_at_period_end is false + subscription = sub + if userID? and sub.metadata?.id is userID + unless subscription?.cancel_at_period_end is false + subscription = sub + + # Check for backwards compatible basic subscription search + # Only recipient subscriptions are currently searched for via userID + if userID? and not sub.metadata?.id and sub.plan?.id is 'basic' + subscription ?= sub + + return done(subscription) if subscription?.cancel_at_period_end is false + + if subscriptions.has_more + nextBatch(subscriptions.data[subscriptions.data.length - 1].id, done) + else + done(subscription) + nextBatch(null, done) + + getAnalyticsStringID: (str, callback) -> + unless str? + log.error "getAnalyticsStringID given invalid str param" + return callback -1 + @analyticsStringCache ?= {} + return callback @analyticsStringCache[str] if @analyticsStringCache[str] + + insertString = => + # http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/#auto-increment-optimistic-loop + AnalyticsString.find({}, {_id: 1}).sort({_id: -1}).limit(1).exec (err, documents) => + if err? + log.error "Failed to find next analytics string _id for #{str}" + return callback -1 + seq = if documents.length > 0 then documents[0]._id + 1 else 1 + doc = new AnalyticsString _id: seq, v: str + doc.save (err) => + if err? + log.error "Failed to save analytics string ID for #{str}" + return callback -1 + @analyticsStringCache[str] = seq + callback seq + + # Find existing string + AnalyticsString.findOne(v: str).exec (err, document) => + if err? + log.error "Failed to lookup analytics string #{str}" + return callback -1 + if document + @analyticsStringCache[str] = document._id + return callback @analyticsStringCache[str] + insertString() diff --git a/server/mail/sent/MailSent.coffee b/server/mail/sent/MailSent.coffee index f8479033a..f87bc6743 100644 --- a/server/mail/sent/MailSent.coffee +++ b/server/mail/sent/MailSent.coffee @@ -8,4 +8,6 @@ MailSent = new mongoose.Schema({ 'default': Date.now }, {strict: false}) +MailSent.index({user: 1}, {name: 'User'}) + module.exports = MailSent = mongoose.model('mail.sent', MailSent) diff --git a/server/patches/Patch.coffee b/server/patches/Patch.coffee index 51d4fea97..712d7cdb3 100644 --- a/server/patches/Patch.coffee +++ b/server/patches/Patch.coffee @@ -2,8 +2,9 @@ mongoose = require('mongoose') deltas = require '../../app/core/deltas' log = require 'winston' {handlers} = require '../commons/mapping' +config = require '../../server_config' -PatchSchema = new mongoose.Schema({status: String}, {strict: false}) +PatchSchema = new mongoose.Schema({status: String}, {strict: false,read:config.mongo.readpref}) PatchSchema.pre 'save', (next) -> return next() unless @isNew # patch can't be altered after creation, so only need to check data once diff --git a/server/patches/patch_handler.coffee b/server/patches/patch_handler.coffee index 3a6cd4ff7..63debc894 100644 --- a/server/patches/patch_handler.coffee +++ b/server/patches/patch_handler.coffee @@ -25,6 +25,17 @@ PatchHandler = class PatchHandler extends Handler return @setStatus(req, res, args[0]) if req.route.method is 'put' and args[1] is 'status' super(arguments...) + get: (req, res) -> + if req.query.view in ['pending'] + query = status: 'pending' + q = Patch.find(query) + q.exec (err, documents) => + return @sendDatabaseError(res, err) if err + documents = (@formatEntity(req, doc) for doc in documents) + @sendSuccess(res, documents) + else + super(arguments...) + setStatus: (req, res, id) -> newStatus = req.body.status unless newStatus in ['rejected', 'accepted', 'withdrawn'] @@ -37,7 +48,7 @@ PatchHandler = class PatchHandler extends Handler targetHandler = require('../' + handlers[targetInfo.collection]) targetModel = targetHandler.modelClass - query = { 'original': targetInfo.original } + query = { $or: [{'original': targetInfo.original}, {'_id': mongoose.Types.ObjectId(targetInfo.original)}] } sort = { 'version.major': -1, 'version.minor': -1 } targetModel.findOne(query).sort(sort).exec (err, target) => return @sendDatabaseError(res, err) if err @@ -79,7 +90,8 @@ PatchHandler = class PatchHandler extends Handler docLink = "http://codecombat.com#{req.headers['x-current-path']}" @sendPatchCreatedHipChatMessage creator: req.user, patch: doc, target: doc.targetLoaded, docLink: docLink watchers = doc.targetLoaded.get('watchers') or [] - watchers = (w for w in watchers when not w.equals(req.user.get('_id'))) + # Don't send these emails to the person who submitted the patch, or to Nick, George, or Scott. + watchers = (w for w in watchers when not w.equals(req.user.get('_id')) and not (w + '' in ['512ef4805a67a8c507000001', '5162fab9c92b4c751e000274', '51538fdb812dd9af02000001'])) return unless watchers?.length User.find({_id: {$in: watchers}}).select({email: 1, name: 1}).exec (err, watchers) => for watcher in watchers @@ -101,6 +113,6 @@ PatchHandler = class PatchHandler extends Handler sendPatchCreatedHipChatMessage: (options) -> message = "#{options.creator.get('name')} submitted a patch to <a href=\"#{options.docLink}\">#{options.target.get('name')}</a>: #{options.patch.get('commitMessage')}" - hipchat.sendHipChatMessage message + hipchat.sendHipChatMessage message, ['main'] module.exports = new PatchHandler() diff --git a/server/payments/Payment.coffee b/server/payments/Payment.coffee index f32e8954d..c29814ed2 100644 --- a/server/payments/Payment.coffee +++ b/server/payments/Payment.coffee @@ -1,6 +1,7 @@ mongoose = require('mongoose') +config = require '../../server_config' -PaymentSchema = new mongoose.Schema({}, {strict: false}) +PaymentSchema = new mongoose.Schema({}, {strict: false, read:config.mongo.readpref}) PaymentSchema.index({recipient: 1, 'stripe.timestamp': 1, 'ios.transactionID'}, {unique: true, name: 'unique payment'}) module.exports = mongoose.model('payment', PaymentSchema) diff --git a/server/payments/discount_handler.coffee b/server/payments/discount_handler.coffee new file mode 100644 index 000000000..64e865c94 --- /dev/null +++ b/server/payments/discount_handler.coffee @@ -0,0 +1,48 @@ +# Not paired with a document in the DB, just handles coordinating between +# the stripe property in the user with what's being stored in Stripe. + +Handler = require '../commons/Handler' +config = require '../../server_config' +stripe = require('stripe')(config.stripe.secretKey) + +class DiscountHandler extends Handler + logDiscountError: (req, msg) -> + console.warn "Discount Error: #{req.user.get('slug')} (#{req.user._id}): '#{msg}'" + + discountUser: (req, user, done) -> + if (not user) or user.isAnonymous() + return done({res: 'User must not be anonymous.', code: 403}) + + couponID = req.body.stripe.couponID + if not couponID + @logDiscountError(req, 'Missing couponID.') + return done({res: 'Missing couponID.', code: 422}) + + stripe.coupons.retrieve couponID, (err, coupon) => + if (err) + return done({res: 'No coupon with id '+couponID, code: 404}) + + if customerID = user.get('stripe')?.customerID + options = { coupon: coupon.id } + stripe.customers.update customerID, options, (err, customer) => + if err + @logDiscountError(req, 'Error applying coupon to customer'+customerID) + return done({res: 'Error applying coupon to customer.', code: 500}) + done() + + else + # couponID will be set on the user by the handler + done() + + removeDiscountFromCustomer: (req, user, done) -> + customerID = user.get('stripe').customerID + return done() unless customerID + + stripe.customers.deleteDiscount customerID, (err, customer) => + if err + console.log 'err?', err + @logDiscountError(req, 'Error removing coupon from customer ' + customerID) + return done({res: 'Error applying coupon to customer.', code: 500}) + done() + +module.exports = new DiscountHandler() \ No newline at end of file diff --git a/server/payments/payment_handler.coffee b/server/payments/payment_handler.coffee index ccd2e206f..fa3672297 100644 --- a/server/payments/payment_handler.coffee +++ b/server/payments/payment_handler.coffee @@ -8,7 +8,6 @@ sendwithus = require '../sendwithus' hipchat = require '../hipchat' config = require '../../server_config' request = require 'request' -stripe = require('stripe')(config.stripe.secretKey) async = require 'async' products = { @@ -29,6 +28,11 @@ products = { gems: 25000 id: 'gems_20' } + + 'custom': { + # amount expected in request body + id: 'custom' + } } PaymentHandler = class PaymentHandler extends Handler @@ -37,6 +41,17 @@ PaymentHandler = class PaymentHandler extends Handler postEditableProperties: ['purchased'] jsonSchema: require '../../app/schemas/models/payment.schema' + get: (req, res) -> + return res.send([]) unless req.user + q = Payment.find({recipient:req.user._id}) + q.exec((err, payments) -> + return @sendDatabaseError(res, err) if err + res.send(payments) + ) + + logPaymentError: (req, msg) -> + console.warn "Payment Error: #{req.user.get('slug')} (#{req.user._id}): '#{msg}'" + makeNewInstance: (req) -> payment = super(req) payment.set 'purchaser', req.user._id @@ -44,7 +59,13 @@ PaymentHandler = class PaymentHandler extends Handler payment.set 'created', new Date().toISOString() payment - post: (req, res) -> + post: (req, res, pathName) -> + if pathName is 'check-stripe-charges' + return @checkStripeCharges(req, res) + + if (not req.user) or req.user.isAnonymous() + return @sendForbiddenError(res) + appleReceipt = req.body.apple?.rawReceipt appleTransactionID = req.body.apple?.transactionID appleLocalPrice = req.body.apple?.localPrice @@ -52,24 +73,29 @@ PaymentHandler = class PaymentHandler extends Handler stripeTimestamp = parseInt(req.body.stripe?.timestamp) productID = req.body.productID + if pathName is 'custom' + return @handleStripePaymentPost(req, res, stripeTimestamp, 'custom', stripeToken) + if not (appleReceipt or (stripeTimestamp and productID)) + @logPaymentError(req, "Missing data. Apple? #{!!appleReceipt}. Stripe timestamp? #{!!stripeTimestamp}. Product id? #{!!productID}.") return @sendBadInputError(res, 'Need either apple.rawReceipt or stripe.timestamp and productID') if stripeTimestamp and not productID + @logPaymentError(req, 'Missing stripe productID') return @sendBadInputError(res, 'Need productID if paying with Stripe.') - if stripeTimestamp and (not stripeToken) and (not user.get('stripeCustomerID')) + if stripeTimestamp and (not stripeToken) and (not req.user.get('stripe')?.customerID) + @logPaymentError(req, 'Missing stripe token') return @sendBadInputError(res, 'Need stripe.token if new customer.') if appleReceipt if not appleTransactionID + @logPaymentError(req, 'Missing apple transaction id') return @sendBadInputError(res, 'Apple purchase? Need to specify which transaction.') @handleApplePaymentPost(req, res, appleReceipt, appleTransactionID, appleLocalPrice) - else @handleStripePaymentPost(req, res, stripeTimestamp, productID, stripeToken) - #- Apple payments handleApplePaymentPost: (req, res, receipt, transactionID, localPrice) -> @@ -80,11 +106,14 @@ PaymentHandler = class PaymentHandler extends Handler verifyReq = request.post({url: config.apple.verifyURL, json: formFields}, (err, verifyRes, body) => if err or not body?.receipt?.in_app or (not body?.bundle_id is 'com.codecombat.CodeCombat') console.warn 'apple receipt error?', err, body + @logPaymentError(req, 'Unable to verify apple receipt') @sendBadInputError(res, 'Unable to verify Apple receipt.') return transaction = _.find body.receipt.in_app, { transaction_id: transactionID } - return @sendBadInputError(res, 'Invalid transactionID.') unless transaction + unless transaction + @logPaymentError(req, 'Missing transaction given id.') + return @sendBadInputError(res, 'Invalid transactionID.') #- Check existence transactionID = transaction.transaction_id @@ -93,10 +122,13 @@ PaymentHandler = class PaymentHandler extends Handler if payment unless payment.get('recipient').equals(req.user._id) + @logPaymentError(req, 'Cross user apple payment.') return @sendForbiddenError(res) @recalculateGemsFor(req.user, (err) => - return @sendDatabaseError(res, err) if err + if err + @logPaymentError(req, 'Apple recalc db error.'+err) + return @sendDatabaseError(res, err) @sendSuccess(res, @formatEntity(req, payment)) ) return @@ -114,11 +146,18 @@ PaymentHandler = class PaymentHandler extends Handler } validation = @validateDocumentInput(payment.toObject()) - return @sendBadInputError(res, validation.errors) if validation.valid is false + if validation.valid is false + @logPaymentError(req, 'Invalid apple payment object.') + return @sendBadInputError(res, validation.errors) + payment.save((err) => - return @sendDatabaseError(res, err) if err + if err + @logPaymentError(req, 'Apple payment save error.'+err) + return @sendDatabaseError(res, err) @incrementGemsFor(req.user, product.gems, (err) => - return @sendDatabaseError(res, err) if err + if err + @logPaymentError(req, 'Apple incr db error.'+err) + return @sendDatabaseError(res, err) @sendPaymentHipChatMessage user: req.user, payment: payment @sendCreated(res, @formatEntity(req, payment)) ) @@ -126,26 +165,39 @@ PaymentHandler = class PaymentHandler extends Handler ) ) - #- Stripe payments handleStripePaymentPost: (req, res, timestamp, productID, token) -> # First, make sure we save the payment info as a Customer object, if we haven't already. - if not req.user.get('stripeCustomerID') - stripe.customers.create({ - card: token - description: req.user._id + '' - }).then(((customer) => - req.user.set('stripeCustomerID', customer.id) - req.user.save((err) => - return @sendDatabaseError(res, err) if err + if token + customerID = req.user.get('stripe')?.customerID + + if customerID + # old customer, new token. Save it. + stripe.customers.update customerID, { card: token }, (err, customer) => @beginStripePayment(req, res, timestamp, productID) - ) - ), - (err) => - return @sendDatabaseError(res, err) - ) + + else + newCustomer = { + card: token + email: req.user.get('email') + metadata: { id: req.user._id + '', slug: req.user.get('slug') } + } + + stripe.customers.create newCustomer, (err, customer) => + if err + @logPaymentError(req, 'Stripe customer creation error. '+err) + return @sendDatabaseError(res, err) + + stripeInfo = _.cloneDeep(req.user.get('stripe') ? {}) + stripeInfo.customerID = customer.id + req.user.set('stripe', stripeInfo) + req.user.save (err) => + if err + @logPaymentError(req, 'Stripe customer id save db error. '+err) + return @sendDatabaseError(res, err) + @beginStripePayment(req, res, timestamp, productID) else @beginStripePayment(req, res, timestamp, productID) @@ -162,7 +214,7 @@ PaymentHandler = class PaymentHandler extends Handler ) ), ((callback) -> - stripe.charges.list({customer: req.user.get('stripeCustomerID')}, (err, recentCharges) => + stripe.charges.list({customer: req.user.get('stripe')?.customerID}, (err, recentCharges) => return callback(err) if err charge = _.find recentCharges.data, (c) -> c.metadata.timestamp is timestamp callback(null, charge) @@ -171,78 +223,129 @@ PaymentHandler = class PaymentHandler extends Handler ], ((err, results) => - return @sendDatabaseError(res, err) if err + if err + @logPaymentError(req, 'Stripe async load db error. '+err) + return @sendDatabaseError(res, err) [payment, charge] = results if not (payment or charge) # Proceed normally from the beginning - @chargeStripe(req, res, payment, product) + @chargeStripe(req, res, product) else if charge and not payment # Initialized Payment. Start from charging. - @recordStripeCharge(req, res, payment, product, charge) + @recordStripeCharge(req, res, charge) else + return @sendSuccess(res, @formatEntity(req, payment)) if product.id is 'custom' + # Charged Stripe and recorded it. Recalculate gems to make sure credited the purchase. @recalculateGemsFor(req.user, (err) => - return @sendDatabaseError(res, err) if err + if err + @logPaymentError(req, 'Stripe recalc db error. '+err) + return @sendDatabaseError(res, err) @sendPaymentHipChatMessage user: req.user, payment: payment @sendSuccess(res, @formatEntity(req, payment)) ) ) ) + chargeStripe: (req, res, product) -> + amount = parseInt product.amount ? req.body.amount + return @sendError(res, 400, "Invalid amount.") if isNaN(amount) - chargeStripe: (req, res, payment, product) -> stripe.charges.create({ - amount: product.amount + amount: amount currency: 'usd' - customer: req.user.get('stripeCustomerID') + customer: req.user.get('stripe')?.customerID metadata: { productID: product.id userID: req.user._id + '' gems: product.gems timestamp: parseInt(req.body.stripe?.timestamp) + description: req.body.description } receipt_email: req.user.get('email') + statement_descriptor: 'CODECOMBAT.COM' }).then( # success case - ((charge) => @recordStripeCharge(req, res, payment, product, charge)), + ((charge) => @recordStripeCharge(req, res, charge)), # error case ((err) => if err.type in ['StripeCardError', 'StripeInvalidRequestError'] @sendError(res, 402, err.message) else + @logPaymentError(req, 'Stripe charge error. '+err) @sendDatabaseError(res, 'Error charging card, please retry.')) ) - - recordStripeCharge: (req, res, payment, product, charge) -> + recordStripeCharge: (req, res, charge) -> return @sendError(res, 500, 'Fake db error for testing.') if req.body.breakAfterCharging payment = @makeNewInstance(req) payment.set 'service', 'stripe' - payment.set 'productID', req.body.productID - payment.set 'amount', product.amount - payment.set 'gems', product.gems + payment.set 'productID', charge.metadata.productID + payment.set 'amount', parseInt(charge.amount) + payment.set 'gems', parseInt(charge.metadata.gems) if charge.metadata.gems + payment.set 'description', charge.metadata.description if charge.metadata.description payment.set 'stripe', { - customerID: req.user.get('stripeCustomerID') - timestamp: parseInt(req.body.stripe.timestamp) + customerID: charge.customer + timestamp: parseInt(charge.metadata.timestamp) chargeID: charge.id } validation = @validateDocumentInput(payment.toObject()) - return @sendBadInputError(res, validation.errors) if validation.valid is false + if validation.valid is false + @logPaymentError(req, 'Invalid stripe payment object.') + return @sendBadInputError(res, validation.errors) payment.save((err) => + return @sendDatabaseError(res, err) if err + return @sendCreated(res, @formatEntity(req, payment)) if payment.productID is 'custom' # Credit gems - return @sendDatabaseError(res, err) if err - @incrementGemsFor(req.user, product.gems, (err) => - return @sendDatabaseError(res, err) if err + @incrementGemsFor(req.user, parseInt(charge.metadata.gems), (err) => + if err + @logPaymentError(req, 'Stripe incr db error. '+err) + return @sendDatabaseError(res, err) @sendCreated(res, @formatEntity(req, payment)) ) ) + #- Confirm all Stripe charges are recorded on our server + + checkStripeCharges: (req, res) -> + return @sendSuccess(res) unless customerID = req.user.get('stripe')?.customerID + async.parallel([ + ((callback) -> + criteria = { recipient: req.user._id, 'stripe.invoiceID': { $exists: false }, 'ios.transactionID': { $exists: false } } + Payment.find(criteria).limit(100).sort({_id:-1}).exec((err, payments) => + callback(err, payments) + ) + ), + ((callback) -> + stripe.charges.list({customer: customerID, limit: 100}, (err, recentCharges) => + return callback(err) if err + callback(null, recentCharges.data) + ) + ) + ], + + ((err, results) => + if err + @logPaymentError(req, 'Stripe async load db error. '+err) + return @sendDatabaseError(res, err) + + [payments, charges] = results + recordedChargeIDs = (p.get('stripe').chargeID for p in payments) + for charge in charges + continue unless charge.paid + continue if charge.invoice # filter out subscription charges + if charge.id not in recordedChargeIDs + return @recordStripeCharge(req, res, charge) + + @sendSuccess(res) + ) + ) #- Incrementing/recalculating gems @@ -257,22 +360,26 @@ PaymentHandler = class PaymentHandler extends Handler else user.update({$inc: {'purchased.gems': gems}}, {}, (err) -> done(err)) - recalculateGemsFor: (user, done) -> + recalculateGemsFor: (user, done, saveIfUnchanged=true) -> Payment.find({recipient: user._id}).select('gems').exec((err, payments) -> gems = _.reduce payments, ((sum, p) -> sum + p.get('gems')), 0 purchased = _.clone(user.get('purchased')) purchased ?= {} + if (purchased.gems or 0) isnt gems + log.debug "Updating #{user.get('_id')} gems from #{purchased.gems} to #{gems} from #{payments.length} payments; #{user.get('email')} #{user.get('name')}" + else unless saveIfUnchanged + log.debug "#{user.get('_id')} already had #{purchased.gems} #{gems} from #{payments.length} payments; #{user.get('email')} #{user.get('name')}" + return done() purchased.gems = gems user.set('purchased', purchased) user.save((err) -> done(err)) - ) sendPaymentHipChatMessage: (options) -> try message = "#{options.user?.get('name')} bought #{options.payment?.get('amount')} via #{options.payment?.get('service')}." - hipchat.sendHipChatMessage message + hipchat.sendHipChatMessage message, ['tower'] catch e log.error "Couldn't send HipChat message on payment because of error: #{e}" diff --git a/server/payments/subscription_handler.coffee b/server/payments/subscription_handler.coffee new file mode 100644 index 000000000..1571a9de7 --- /dev/null +++ b/server/payments/subscription_handler.coffee @@ -0,0 +1,530 @@ +# Not paired with a document in the DB, just handles coordinating between +# the stripe property in the user with what's being stored in Stripe. + +log = require 'winston' +MongoClient = require('mongodb').MongoClient +mongoose = require 'mongoose' +async = require 'async' +config = require '../../server_config' +Handler = require '../commons/Handler' +discountHandler = require './discount_handler' +Prepaid = require '../prepaids/Prepaid' +User = require '../users/User' +{findStripeSubscription} = require '../lib/utils' +{getSponsoredSubsAmount} = require '../../app/core/utils' + +recipientCouponID = 'free' + +# TODO: rename this to avoid collisions with 'subscriptions' variables +subscriptions = { + basic: { + gems: 3500 + amount: 999 # For calculating incremental quantity before sub creation + } +} + +class SubscriptionHandler extends Handler + logSubscriptionError: (user, msg) -> + console.warn "Subscription Error: #{user.get('slug')} (#{user._id}): '#{msg}'" + + getByRelationship: (req, res, args...) -> + return @getStripeEvents(req, res) if args[1] is 'stripe_events' + return @getStripeInvoices(req, res) if args[1] is 'stripe_invoices' + return @getStripeSubscriptions(req, res) if args[1] is 'stripe_subscriptions' + return @getSubscribers(req, res) if args[1] is 'subscribers' + super(arguments...) + + getStripeEvents: (req, res) -> + # console.log 'subscription_handler getStripeEvents', req.body?.options + return @sendForbiddenError(res) unless req.user?.isAdmin() + stripe.events.list req.body.options, (err, events) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, events) + + getStripeInvoices: (req, res) -> + # console.log 'subscription_handler getStripeInvoices' + return @sendForbiddenError(res) unless req.user?.isAdmin() + + stripe.invoices.list req.body.options, (err, invoices) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, invoices) + + getStripeSubscriptions: (req, res) -> + # console.log 'subscription_handler getStripeSubscriptions' + return @sendForbiddenError(res) unless req.user?.isAdmin() + stripeSubscriptions = [] + createGetSubFn = (customerID, subscriptionID) => + (done) => + stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) => + # TODO: return error instead of ignore? + stripeSubscriptions.push(subscription) unless err + done() + tasks = [] + for subscription in req.body.subscriptions + tasks.push createGetSubFn(subscription.customerID, subscription.subscriptionID) + async.parallel tasks, (err, results) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, stripeSubscriptions) + + getSubscribers: (req, res) -> + # console.log 'subscription_handler getSubscribers' + return @sendForbiddenError(res) unless req.user?.isAdmin() + subscriberUserIDs = req.body.ids or [] + + User.find {_id: {$in: subscriberUserIDs}}, (err, users) => + return @sendDatabaseError(res, err) if err + userMap = {} + userMap[user.id] = user.toObject() for user in users + + try + # Get conversion data directly from analytics database and add it to results + url = "mongodb://#{config.mongo.analytics_host}:#{config.mongo.analytics_port}/#{config.mongo.analytics_db}" + MongoClient.connect url, (err, db) => + if err + log.debug 'Analytics connect error: ' + err + return @sendDatabaseError(res, err) + userEventMap = {} + events = ['Finished subscription purchase', 'Show subscription modal'] + query = {$and: [{user: {$in: subscriberUserIDs}}, {event: {$in: events}}]} + db.collection('log').find(query).sort({_id: -1}).each (err, doc) => + if err + db.close() + return @sendDatabaseError(res, err) + if (doc) + userEventMap[doc.user] ?= [] + userEventMap[doc.user].push doc + else + db.close() + for userID, eventList of userEventMap + finishedPurchase = false + for event in eventList + finishedPurchase = true if event.event is 'Finished subscription purchase' + if finishedPurchase + if event.event is 'Show subscription modal' and event.properties?.level? + userMap[userID].conversion = event.properties.level + break + else if event.event is 'Show subscription modal' and event.properties?.label in ['buy gems modal', 'check private clan', 'create clan'] + userMap[userID].conversion = event.properties.label + break + @sendSuccess(res, userMap) + catch err + log.debug 'Analytics error:\n' + err + @sendSuccess(res, userMap) + + subscribeUser: (req, user, done) -> + if (not req.user) or req.user.isAnonymous() or user.isAnonymous() + return done({res: 'You must be signed in to subscribe.', code: 403}) + + token = req.body.stripe.token + prepaidCode = req.body.stripe.prepaidCode + customerID = user.get('stripe')?.customerID + if not (token or customerID or prepaidCode) + @logSubscriptionError(user, 'Missing Stripe token or customer ID or prepaid code') + return done({res: 'Missing Stripe token or customer ID or prepaid code', code: 422}) + + # Get Stripe customer + if customerID + if token + stripe.customers.update customerID, { card: token }, (err, customer) => + if err or not customer + # should not happen outside of test and production polluting each other + @logSubscriptionError(user, 'Cannot find customer: ' + customerID + '\n\n' + err) + return done({res: 'Cannot find customer.', code: 404}) + @checkForCoupon(req, user, customer, done) + else + stripe.customers.retrieve customerID, (err, customer) => + if err + @logSubscriptionError(user, 'Stripe customer retrieve error. ' + err) + return done({res: 'Database error.', code: 500}) + @checkForCoupon(req, user, customer, done) + else + options = + email: user.get('email') + metadata: { id: user._id + '', slug: user.get('slug') } + options.card = token if token? + stripe.customers.create options, (err, customer) => + if err + if err.type in ['StripeCardError', 'StripeInvalidRequestError'] + return done({res: 'Card error', code: 402}) + else + @logSubscriptionError(user, 'Stripe customer creation error. ' + err) + return done({res: 'Database error.', code: 500}) + + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + stripeInfo.customerID = customer.id + user.set('stripe', stripeInfo) + user.save (err) => + if err + @logSubscriptionError(user, 'Stripe customer id save db error. ' + err) + return done({res: 'Database error.', code: 500}) + @checkForCoupon(req, user, customer, done) + + checkForCoupon: (req, user, customer, done) -> + # Check if user is subscribing someone else + if req.body.stripe?.subscribeEmails? + return @updateStripeRecipientSubscriptions req, user, customer, done + + if user.get('stripe')?.sponsorID + return done({res: 'You already have a sponsored subscription.', code: 403}) + + if req.body?.stripe?.prepaidCode + Prepaid.findOne code: req.body.stripe.prepaidCode, (err, prepaid) => + if err + @logSubscriptionError(user, 'Prepaid lookup error. ' + err) + return done({res: 'Database error.', code: 500}) + return done({res: 'Prepaid not found', code: 404}) unless prepaid? + return done({res: 'Prepaid not for subscription', code: 403}) unless prepaid.get('type') is 'subscription' + return done({res: 'Prepaid has already been used', code: 403}) unless prepaid.get('status') is 'active' + return done({res: 'Database error.', code: 500}) unless prepaid.get('properties')?.couponID + couponID = prepaid.get('properties').couponID + + # Update user + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + stripeInfo.couponID = couponID + stripeInfo.prepaidCode = req.body.stripe.prepaidCode + user.set('stripe', stripeInfo) + @checkForExistingSubscription(req, user, customer, couponID, done) + else + couponID = user.get('stripe')?.couponID + # SALE LOGIC + # overwrite couponID with another for everyone-sales + #couponID = 'hoc_399' if not couponID + @checkForExistingSubscription(req, user, customer, couponID, done) + + checkForExistingSubscription: (req, user, customer, couponID, done) -> + findStripeSubscription customer.id, subscriptionID: user.get('stripe')?.subscriptionID, (subscription) => + + if subscription + + if subscription.cancel_at_period_end + # Things are a little tricky here. Can't re-enable a cancelled subscription, + # so it needs to be deleted, but also don't want to charge for the new subscription immediately. + # So delete the cancelled subscription (no at_period_end given here) and give the new + # subscription a trial period that ends when the cancelled subscription would have ended. + stripe.customers.cancelSubscription subscription.customer, subscription.id, (err) => + if err + @logSubscriptionError(user, 'Stripe cancel subscription error. ' + err) + return done({res: 'Database error.', code: 500}) + options = { plan: 'basic', metadata: {id: user.id}, trial_end: subscription.current_period_end } + options.coupon = couponID if couponID + stripe.customers.createSubscription customer.id, options, (err, subscription) => + if err + @logSubscriptionError(user, 'Stripe customer plan setting error. ' + err) + return done({res: 'Database error.', code: 500}) + @updateUser(req, user, customer, subscription, false, done) + + else if couponID + # Update subscription with given couponID + stripe.customers.updateSubscription customer.id, subscription.id, coupon: couponID, (err, subscription) => + if err + @logSubscriptionError(user, 'Stripe update subscription coupon error. ' + err) + return done({res: 'Database error.', code: 500}) + @updateUser(req, user, customer, subscription, false, done) + + else + # Skip creating the subscription + @updateUser(req, user, customer, subscription, false, done) + + else + options = { plan: 'basic', metadata: {id: user.id}} + options.coupon = couponID if couponID + stripe.customers.createSubscription customer.id, options, (err, subscription) => + if err + @logSubscriptionError(user, 'Stripe customer plan setting error. ' + err) + return done({res: 'Database error.', code: 500}) + + @updateUser(req, user, customer, subscription, true, done) + + updateUser: (req, user, customer, subscription, increment, done) -> + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + stripeInfo.planID = 'basic' + stripeInfo.subscriptionID = subscription.id + stripeInfo.customerID = customer.id + + # To make sure things work for admins, who are mad with power + # And, so Handler.saveChangesToDocument doesn't undo all our saves here + req.body.stripe = stripeInfo + user.set('stripe', stripeInfo) + + if increment + purchased = _.clone(user.get('purchased')) + purchased ?= {} + purchased.gems ?= 0 + purchased.gems += subscriptions.basic.gems # TODO: Put actual subscription amount here + user.set('purchased', purchased) + + user.save (err) => + if err + @logSubscriptionError(user, 'Stripe user plan saving error. ' + err) + return done({res: 'Database error.', code: 500}) + + if stripeInfo.prepaidCode? + # Update prepaid to 'used' + Prepaid.findOne code: stripeInfo.prepaidCode, (err, prepaid) => + if err + @logSubscriptionError(user, 'Prepaid find error. ' + err) + return done({res: 'Database error.', code: 500}) + unless prepaid? + @logSubscriptionError(user, "Expected prepaid not found: #{stripeInfo.prepaidCode}") + return done({res: 'Database error.', code: 500}) + prepaid.set('status', 'used') + prepaid.set('redeemer', user.get('_id')) + prepaid.save (err) => + if err + @logSubscriptionError(user, 'Prepaid update error. ' + err) + return done({res: 'Database error.', code: 500}) + done() + else + done() + + updateStripeRecipientSubscriptions: (req, user, customer, done) -> + return done({res: 'Database error.', code: 500}) unless req.body.stripe?.subscribeEmails? + + emails = req.body.stripe.subscribeEmails.map((email) -> email.trim().toLowerCase() unless _.isEmpty(email)) + _.remove(emails, (email) -> _.isEmpty(email)) + + User.find {emailLower: {$in: emails}}, (err, recipients) => + if err + @logSubscriptionError(user, "User lookup error. " + err) + return done({res: 'Database error.', code: 500}) + + createUpdateFn = (recipient) => + (done) => + # Find existing recipient subscription + findStripeSubscription customer.id, userID: recipient.id, (subscription) => + + if subscription + if subscription.cancel_at_period_end + # Things are a little tricky here. Can't re-enable a cancelled subscription, + # so it needs to be deleted, but also don't want to charge for the new subscription immediately. + # So delete the cancelled subscription (no at_period_end given here) and give the new + # subscription a trial period that ends when the cancelled subscription would have ended. + stripe.customers.cancelSubscription subscription.customer, subscription.id, (err) => + if err + @logSubscriptionError(user, 'Stripe cancel subscription error. ' + err) + return done({res: 'Database error.', code: 500}) + + options = + plan: 'basic' + coupon: recipientCouponID + metadata: {id: recipient.id} + trial_end: subscription.current_period_end + stripe.customers.createSubscription customer.id, options, (err, subscription) => + if err + @logSubscriptionError(user, 'Stripe new subscription error. ' + err) + return done({res: 'Database error.', code: 500}) + done(null, recipient: recipient, subscription: subscription, increment: false) + else + # Can skip creating the subscription + done(null, recipient: recipient, subscription: subscription, increment: false) + + else + options = + plan: 'basic' + coupon: recipientCouponID + metadata: {id: recipient.id} + stripe.customers.createSubscription customer.id, options, (err, subscription) => + if err + @logSubscriptionError(user, 'Stripe new subscription error. ' + err) + return done({res: 'Database error.', code: 500}) + done(null, recipient: recipient, subscription: subscription, increment: true) + + tasks = [] + for recipient in recipients + continue if recipient.id is user.id + continue if recipient.get('stripe')?.subscriptionID? + continue if recipient.get('stripe')?.sponsorID? and recipient.get('stripe')?.sponsorID isnt user.id + tasks.push createUpdateFn(recipient) + + # NOTE: async.parallel yields this error: + # Subscription Error: user23 (54fe3c8fea98978efa469f3b): 'Stripe new subscription error. Error: Request rate limit exceeded' + async.series tasks, (err, results) => + return done(err) if err + @updateCocoRecipientSubscriptions(req, user, customer, results, done) + + updateCocoRecipientSubscriptions: (req, user, customer, stripeRecipients, done) -> + # Update recipients list + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + stripeInfo.recipients ?= [] + stripeRecipientIDs = (sub.recipient.id for sub in stripeRecipients) + _.remove(stripeInfo.recipients, (s) -> s.userID in stripeRecipientIDs) + for sub in stripeRecipients + stripeInfo.recipients.push + userID: sub.recipient.id + subscriptionID: sub.subscription.id + couponID: recipientCouponID + + # TODO: how does token get removed for personal subs? + delete stripeInfo.subscribeEmails + delete stripeInfo.token + req.body.stripe = stripeInfo + user.set('stripe', stripeInfo) + user.save (err) => + if err + @logSubscriptionError(user, 'User saving stripe error. ' + err) + return done({res: 'Database error.', code: 500}) + + createUpdateFn = (recipient, increment) => + (done) => + # Update recipient + stripeInfo = _.cloneDeep(recipient.get('stripe') ? {}) + stripeInfo.sponsorID = user.id + recipient.set 'stripe', stripeInfo + if increment + purchased = _.clone(recipient.get('purchased')) + purchased ?= {} + purchased.gems ?= 0 + purchased.gems += subscriptions.basic.gems + recipient.set('purchased', purchased) + recipient.save (err) => + if err + @logSubscriptionError(user, 'Stripe user saving stripe error. ' + err) + return done({res: 'Database error.', code: 500}) + done() + + tasks = [] + for sub in stripeRecipients + tasks.push createUpdateFn(sub.recipient, sub.increment) + + async.parallel tasks, (err, results) => + return done(err) if err + @updateStripeSponsorSubscription(req, user, customer, done) + + updateStripeSponsorSubscription: (req, user, customer, done) -> + stripeInfo = user.get('stripe') ? {} + numSponsored = stripeInfo.recipients.length + quantity = getSponsoredSubsAmount(subscriptions.basic.amount, numSponsored, stripeInfo.subscriptionID?) + + findStripeSubscription customer.id, subscriptionID: stripeInfo.sponsorSubscriptionID, (subscription) => + if stripeInfo.sponsorSubscriptionID? and not subscription? + @logSubscriptionError(user, "Internal sponsor subscription #{stripeInfo.sponsorSubscriptionID} not found on Stripe customer #{customer.id}") + return done({res: 'Database error.', code: 500}) + + if subscription + return done() if quantity is subscription.quantity # E.g. cancelled sub has been resubbed + + options = quantity: quantity + stripe.customers.updateSubscription customer.id, stripeInfo.sponsorSubscriptionID, options, (err, subscription) => + if err + @logSubscriptionError(user, 'Stripe updating subscription quantity error. ' + err) + return done({res: 'Database error.', code: 500}) + + # Invoice proration immediately + stripe.invoices.create customer: customer.id, (err, invoice) => + if err + @logSubscriptionError(user, 'Stripe proration invoice error. ' + err) + return done({res: 'Database error.', code: 500}) + done() + else + options = + plan: 'incremental' + metadata: {id: user.id} + quantity: quantity + stripe.customers.createSubscription customer.id, options, (err, subscription) => + if err + @logSubscriptionError(user, 'Stripe new subscription error. ' + err) + return done({res: 'Database error.', code: 500}) + @updateCocoSponsorSubscription(req, user, subscription, done) + + updateCocoSponsorSubscription: (req, user, subscription, done) -> + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + stripeInfo.sponsorSubscriptionID = subscription.id + req.body.stripe = stripeInfo + user.set('stripe', stripeInfo) + user.save (err) => + if err + @logSubscriptionError(user, 'Saving user stripe error. ' + err) + return done({res: 'Database error.', code: 500}) + done() + + unsubscribeUser: (req, user, done) -> + # Check if user is subscribing someone else + return @unsubscribeRecipient(req, user, done) if req.body.stripe?.unsubscribeEmail? + + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + stripe.customers.cancelSubscription stripeInfo.customerID, stripeInfo.subscriptionID, { at_period_end: true }, (err) => + if err + @logSubscriptionError(user, 'Stripe cancel subscription error. ' + err) + return done({res: 'Database error.', code: 500}) + delete stripeInfo.planID + user.set('stripe', stripeInfo) + req.body.stripe = stripeInfo + user.save (err) => + if err + @logSubscriptionError(user, 'User save unsubscribe error. ' + err) + return done({res: 'Database error.', code: 500}) + done() + + unsubscribeRecipient: (req, user, done) -> + return done({res: 'Database error.', code: 500}) unless req.body.stripe?.unsubscribeEmail? + + email = req.body.stripe.unsubscribeEmail.trim().toLowerCase() + return done({res: 'Database error.', code: 500}) if _.isEmpty(email) + + deleteUserStripeProp = (user, propName) -> + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + delete stripeInfo[propName] + if _.isEmpty stripeInfo + user.set 'stripe', undefined + else + user.set 'stripe', stripeInfo + + User.findOne {emailLower: email}, (err, recipient) => + if err + @logSubscriptionError(user, "User lookup error. " + err) + return done({res: 'Database error.', code: 500}) + unless recipient + @logSubscriptionError(user, "Recipient #{email} not found.") + return done({res: 'Database error.', code: 500}) + + # Check recipient is currently sponsored + stripeRecipient = recipient.get 'stripe' ? {} + if stripeRecipient?.sponsorID isnt user.id + @logSubscriptionError(user, "Recipient #{recipient.id} not sponsored by #{user.id}. ") + return done({res: 'Can only unsubscribe sponsored subscriptions.', code: 403}) + + # Find recipient subscription + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + for sponsored in stripeInfo.recipients + if sponsored.userID is recipient.id + sponsoredEntry = sponsored + break + unless sponsoredEntry? + @logSubscriptionError(user, 'Unable to find recipient subscription. ') + return done({res: 'Database error.', code: 500}) + + # Update recipient user + deleteUserStripeProp(recipient, 'sponsorID') + recipient.save (err) => + if err + @logSubscriptionError(user, 'Recipient user save unsubscribe error. ' + err) + return done({res: 'Database error.', code: 500}) + + # Cancel Stripe subscription + stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, (err) => + if err + @logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + err) + return done({res: 'Database error.', code: 500}) + + # Update sponsor user + _.remove(stripeInfo.recipients, (s) -> s.userID is recipient.id) + delete stripeInfo.unsubscribeEmail + user.set('stripe', stripeInfo) + req.body.stripe = stripeInfo + user.save (err) => + if err + @logSubscriptionError(user, 'Sponsor user save unsubscribe error. ' + err) + return done({res: 'Database error.', code: 500}) + + return done() unless stripeInfo.sponsorSubscriptionID? + + # Update sponsored subscription quantity + options = + quantity: getSponsoredSubsAmount(subscriptions.basic.amount, stripeInfo.recipients.length, stripeInfo.subscriptionID?) + stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) => + if err + logStripeWebhookError(err) + return done({res: 'Database error.', code: 500}) + done() + +module.exports = new SubscriptionHandler() diff --git a/server/plugins/achievements.coffee b/server/plugins/achievements.coffee index 61d4d9c05..d9772a344 100644 --- a/server/plugins/achievements.coffee +++ b/server/plugins/achievements.coffee @@ -12,41 +12,37 @@ AchievablePlugin = (schema, options) -> User = require '../users/User' # Avoid mutual inclusion cycles Achievement = require '../achievements/Achievement' - before = {} - # Keep track the document before it's saved schema.post 'init', (doc) -> - before[doc.id] = doc.toObject() - # TODO check out how many objects go unreleased + unless doc.unchangedCopy + doc.unchangedCopy = doc.toObject() # Check if an achievement has been earned schema.post 'save', (doc) -> - # sometimes post appears to be called twice. Handle this... - # TODO: Refactor this system to make it request-specific, - # perhaps by having POST/PUT requests store the copy on the request object themselves. - return if doc.isInit('_id') and not (doc.id of before) - isNew = not doc.isInit('_id') or not (doc.id of before) - originalDocObj = before[doc.id] unless isNew + schema.statics.createNewEarnedAchievements doc - if doc.isInit('_id') and not doc.id of before + schema.statics.createNewEarnedAchievements = (doc, unchangedCopy) -> + unchangedCopy ?= doc.unchangedCopy + isNew = not doc.isInit('_id') or not unchangedCopy + + if doc.isInit('_id') and not unchangedCopy log.warn 'document was already initialized but did not go through `init` and is therefore treated as new while it might not be' category = doc.constructor.collection.name loadedAchievements = Achievement.getLoadedAchievements() - #log.debug 'about to save ' + category + ', number of achievements is ' + Object.keys(loadedAchievements).length if category of loadedAchievements + #log.debug 'about to save ' + category + ', number of achievements is ' + loadedAchievements[category].length docObj = doc.toObject() for achievement in loadedAchievements[category] do (achievement) -> query = achievement.get('query') - return log.warn("Empty achievement query for #{achievement.get('name')}.") if _.isEmpty query + return log.error("Empty achievement query for #{achievement.get('name')}.") if _.isEmpty query isRepeatable = achievement.get('proportionalTo')? - alreadyAchieved = if isNew then false else LocalMongo.matchesQuery originalDocObj, query + alreadyAchieved = if isNew then false else LocalMongo.matchesQuery unchangedCopy, query newlyAchieved = LocalMongo.matchesQuery(docObj, query) return unless newlyAchieved and (not alreadyAchieved or isRepeatable) - EarnedAchievement.createForAchievement(achievement, doc, originalDocObj) - - delete before[doc.id] if doc.id of before + #log.info "Making an achievement: #{achievement.get('name')} #{achievement.get('_id')} for doc: #{doc.get('name')} #{doc.get('_id')}" + EarnedAchievement.createForAchievement(achievement, doc, unchangedCopy) module.exports = AchievablePlugin diff --git a/server/plugins/plugins.coffee b/server/plugins/plugins.coffee index 1007719a6..e0dcce596 100644 --- a/server/plugins/plugins.coffee +++ b/server/plugins/plugins.coffee @@ -1,5 +1,4 @@ mongoose = require('mongoose') -textSearch = require('mongoose-text-search') log = require 'winston' utils = require '../lib/utils' @@ -293,7 +292,6 @@ module.exports.SearchablePlugin = (schema, options) -> index[prop] = 'text' for prop in searchable # should now have something like {'index': 1, name: 'text', body: 'text'} - schema.plugin(textSearch) schema.index(index, {sparse: true, name: 'search index', language_override: 'searchLanguage'}) schema.pre 'save', (next) -> @@ -309,17 +307,17 @@ module.exports.SearchablePlugin = (schema, options) -> next() module.exports.TranslationCoveragePlugin = (schema, options) -> - + schema.uses_coco_translation_coverage = true schema.set('autoIndex', true) - + index = {} - + if schema.uses_coco_versions if not schema.uses_coco_names throw Error('If using translation coverage and versioning, should also use names for indexing.') index.slug = 1 - + index.i18nCoverage = 1 - - schema.index(index, {sparse: true, name: 'translation coverage index', background: true}) \ No newline at end of file + + schema.index(index, {sparse: true, name: 'translation coverage index', background: true}) diff --git a/server/polls/Poll.coffee b/server/polls/Poll.coffee new file mode 100644 index 000000000..e6ad7a609 --- /dev/null +++ b/server/polls/Poll.coffee @@ -0,0 +1,35 @@ +mongoose = require 'mongoose' +plugins = require '../plugins/plugins' +jsonSchema = require '../../app/schemas/models/poll.schema' +log = require 'winston' +config = require '../../server_config' +PollSchema = new mongoose.Schema { + created: + type: Date + 'default': Date.now +}, {strict: false, minimize: false,read:config.mongo.readpref} + +PollSchema.index {priority: 1} + +# Just duplicating indexes that get created here by plugins for completeness +PollSchema.index {i18nCoverage: 1}, {name: 'translation coverage index', sparse: true} +PollSchema.index {slug: 1}, {name: 'slug index', sparse: true, unique: true} + +PollSchema.plugin plugins.NamedPlugin +PollSchema.plugin plugins.PatchablePlugin +PollSchema.plugin plugins.TranslationCoveragePlugin +PollSchema.plugin plugins.SearchablePlugin, {searchable: ['name', 'description']} + +PollSchema.statics.privateProperties = [] +PollSchema.statics.editableProperties = [ + 'name' + 'description' + 'answers' + 'i18n' + 'i18nCoverage' + 'priority' + 'userProperty' +] +PollSchema.statics.jsonSchema = jsonSchema + +module.exports = Poll = mongoose.model 'poll', PollSchema, 'polls' diff --git a/server/polls/UserPollsRecord.coffee b/server/polls/UserPollsRecord.coffee new file mode 100644 index 000000000..ea90922a5 --- /dev/null +++ b/server/polls/UserPollsRecord.coffee @@ -0,0 +1,58 @@ +mongoose = require 'mongoose' +plugins = require '../plugins/plugins' +jsonSchema = require '../../app/schemas/models/user-polls-record.schema' +log = require 'winston' +Poll = require './Poll' +User = require '../users/User' + +UserPollsRecordSchema = new mongoose.Schema {}, {strict: false, minimize: false} + +UserPollsRecordSchema.index {user: 1}, {unique: true, name: 'user polls record index'} + +UserPollsRecordSchema.post 'init', (doc) -> + doc.previousPolls ?= _.clone doc.get('polls') ? {} + +UserPollsRecordSchema.pre 'save', (next) -> + return next() unless @previousPolls? + @set 'changed', new Date() + rewards = @get('rewards') ? {} + level = @get('level') ? {} + gemDelta = 0 + for pollID, answer of @get('polls') ? {} + previousAnswer = @previousPolls[pollID] + updatePollVotes @get('user'), pollID, answer, previousAnswer unless answer is previousAnswer + unless rewards[pollID] + rewards[pollID] = reward = random: Math.random(), level: level + gemDelta += Math.ceil 2 * reward.random * reward.level + @set 'rewards', rewards + @markModified 'rewards' + updateUserGems @get('user'), gemDelta if gemDelta + next() + +updatePollVotes = (userID, pollID, answer, previousAnswer) -> + Poll.findById mongoose.Types.ObjectId(pollID), {}, (err, poll) -> + return log.error err if err + answers = poll.get 'answers' + _.find(answers, key: answer)?.votes++ + _.find(answers, key: previousAnswer)?.votes-- if previousAnswer + poll.set 'answers', answers + poll.markModified 'answers' + poll.save (err, newPoll, numberAffected) -> + return log.error err if err + updateUserProperty userID, userProperty, answer if userProperty = poll.get 'userProperty' + +updateUserProperty = (userID, userProperty, answer) -> + update = $set: {"#{userProperty}": answer} + User.update {_id: mongoose.Types.ObjectId(userID)}, update, (err, numberAffected) -> + return log.error err if err + +updateUserGems = (userID, gemDelta) -> + update = $inc: {'earned.gems': gemDelta} + User.update {_id: mongoose.Types.ObjectId(userID)}, update, (err, numberAffected) -> + return log.error err if err + +UserPollsRecordSchema.statics.privateProperties = [] +UserPollsRecordSchema.statics.editableProperties = ['polls'] +UserPollsRecordSchema.statics.jsonSchema = jsonSchema + +module.exports = UserPollsRecord = mongoose.model 'user.polls.record', UserPollsRecordSchema, 'user.polls.records' diff --git a/server/polls/poll_handler.coffee b/server/polls/poll_handler.coffee new file mode 100644 index 000000000..0245e2e92 --- /dev/null +++ b/server/polls/poll_handler.coffee @@ -0,0 +1,57 @@ +Poll = require './Poll' +UserPollsRecord = require './UserPollsRecord' +Handler = require '../commons/Handler' +async = require 'async' +mongoose = require 'mongoose' + +PollHandler = class PollHandler extends Handler + modelClass: Poll + jsonSchema: require '../../app/schemas/models/poll.schema' + allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] + + hasAccess: (req) -> + req.method in ['GET', 'PUT'] or req.user?.isAdmin() + + hasAccessToDocument: (req, document, method=null) -> + method = (method or req.method).toLowerCase() + return true if req.user?.isAdmin() + return true if method is 'get' + return true if method in ['post', 'put'] and @isJustFillingTranslations req, document + false + + getByRelationship: (req, res, args...) -> + relationship = args[1] + return @getNextPoll(req, res, args[0]) if relationship is 'next' + super arguments... + + getNextPoll: (req, res, userPollsRecordID) -> + if userPollsRecordID and userPollsRecordID isnt '-' + UserPollsRecord.findOne(_id: mongoose.Types.ObjectId(userPollsRecordID)).lean().exec (err, userPollsRecord) => + return @sendDatabaseError(res, err) if err + answeredPolls = _.keys(userPollsRecord?.polls ? {}) + @getNextUnansweredPoll req, res, answeredPolls + else + @getNextUnansweredPoll req, res, [] + + getNextUnansweredPoll: (req, res, answeredPolls) -> + if answeredPolls.length + query = {_id: {$nin: (mongoose.Types.ObjectId(pollID) for pollID in answeredPolls)}} + else + query = {} + Poll.findOne(query).sort('priority').exec (err, poll) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless poll + @sendSuccess res, @formatEntity(req, poll) + + delete: (req, res, slugOrID) -> + return @sendForbiddenError res unless req.user?.isAdmin() + @getDocumentForIdOrSlug slugOrID, (err, document) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless document? + document.remove (err, document) => + return @sendDatabaseError(res, err) if err + @sendNoContent res + + getNamesByIDs: (req, res) -> @getNamesByOriginals req, res, true + +module.exports = new PollHandler() diff --git a/server/polls/user_polls_record_handler.coffee b/server/polls/user_polls_record_handler.coffee new file mode 100644 index 000000000..36b81298a --- /dev/null +++ b/server/polls/user_polls_record_handler.coffee @@ -0,0 +1,40 @@ +UserPollsRecord = require './UserPollsRecord' +Handler = require '../commons/Handler' +async = require 'async' +mongoose = require 'mongoose' + +UserPollsRecordHandler = class UserPollsRecordHandler extends Handler + modelClass: UserPollsRecord + jsonSchema: require '../../app/schemas/models/user-polls-record.schema' + + hasAccess: (req) -> + req.user and (req.method in ['GET', 'POST', 'PUT'] or req.user?.isAdmin()) + + hasAccessToDocument: (req, document, method=null) -> + req.user?.isAdmin() or req.user?._id.equals document.get('user') + + getByRelationship: (req, res, args...) -> + relationship = args[1] + return @getUserPollsRecord(req, res, args[2]) if relationship is 'user' + super arguments... + + getUserPollsRecord: (req, res, userID) -> + UserPollsRecord.findOne(user: userID).exec (err, doc) => + return @sendDatabaseError(res, err) if err + return @sendSuccess(res, doc) if doc? + @createAndSaveNewUserPollsRecord userID, req, res + + createAndSaveNewUserPollsRecord: (userID, req, res) => + return @sendForbiddenError(res) unless req.user + initVals = user: userID, polls: {}, level: req.user.level() + userPollsRecord = new UserPollsRecord initVals + userPollsRecord.save (err) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, @formatEntity(req, userPollsRecord)) + + saveChangesToDocument: (req, document, done) -> + document.set 'level', req.user.level() + super req, document, done + + +module.exports = new UserPollsRecordHandler() diff --git a/server/prepaids/Prepaid.coffee b/server/prepaids/Prepaid.coffee new file mode 100644 index 000000000..bccc9ac60 --- /dev/null +++ b/server/prepaids/Prepaid.coffee @@ -0,0 +1,14 @@ +mongoose = require 'mongoose' +config = require '../../server_config' +PrepaidSchema = new mongoose.Schema {}, {strict: false, minimize: false,read:config.mongo.readpref} + +PrepaidSchema.statics.generateNewCode = (done) -> + tryCode = -> + code = _.sample("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8).join('') + Prepaid.findOne code: code, (err, prepaid) -> + return done() if err + return done(code) unless prepaid + tryCode() + tryCode() + +module.exports = Prepaid = mongoose.model('prepaid', PrepaidSchema) diff --git a/server/prepaids/prepaid_handler.coffee b/server/prepaids/prepaid_handler.coffee new file mode 100644 index 000000000..ffbe4e0db --- /dev/null +++ b/server/prepaids/prepaid_handler.coffee @@ -0,0 +1,36 @@ +Handler = require '../commons/Handler' +Prepaid = require './Prepaid' + +# TODO: Should this happen on a save() call instead of a prepaid/-/create post? +# TODO: Probably a better way to create a unique 8 charactor string property using db voodoo + +PrepaidHandler = class PrepaidHandler extends Handler + modelClass: Prepaid + jsonSchema: require '../../app/schemas/models/prepaid.schema' + allowedMethods: ['POST'] + + hasAccess: (req) -> + req.user?.isAdmin() + + getByRelationship: (req, res, args...) -> + relationship = args[1] + return @createPrepaid(req, res) if relationship is 'create' + super arguments... + + createPrepaid: (req, res) -> + return @sendForbiddenError(res) unless @hasAccess(req) + return @sendForbiddenError(res) unless req.body.type is 'subscription' + Prepaid.generateNewCode (code) => + return @sendDatabaseError(res, 'Database error.') unless code + prepaid = new Prepaid + creator: req.user.id + type: req.body.type + status: 'active' + code: code + properties: + couponID: 'free' + prepaid.save (err) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, prepaid.toObject()) + +module.exports = new PrepaidHandler() diff --git a/server/purchases/Purchase.coffee b/server/purchases/Purchase.coffee index 508e5be7b..cb0c18130 100644 --- a/server/purchases/Purchase.coffee +++ b/server/purchases/Purchase.coffee @@ -1,6 +1,6 @@ mongoose = require('mongoose') - -PurchaseSchema = new mongoose.Schema({status: String}, {strict: false}) +config = require '../../server_config' +PurchaseSchema = new mongoose.Schema({status: String}, {strict: false,read:config.mongo.readpref}) PurchaseSchema.index({recipient: 1, 'purchased.original': 1}, {unique: true, name: 'unique purchase'}) module.exports = mongoose.model('purchase', PurchaseSchema) diff --git a/server/purchases/purchase_handler.coffee b/server/purchases/purchase_handler.coffee index 0b50b8439..220252f05 100644 --- a/server/purchases/purchase_handler.coffee +++ b/server/purchases/purchase_handler.coffee @@ -4,8 +4,6 @@ Handler = require '../commons/Handler' {handlers} = require '../commons/mapping' mongoose = require 'mongoose' log = require 'winston' -sendwithus = require '../sendwithus' -hipchat = require '../hipchat' PurchaseHandler = class PurchaseHandler extends Handler modelClass: Purchase @@ -19,22 +17,22 @@ PurchaseHandler = class PurchaseHandler extends Handler purchase.set 'recipient', req.user._id purchase.set 'created', new Date().toISOString() purchase - + post: (req, res) -> purchased = req.body.purchased purchaser = req.user._id purchasedOriginal = purchased?.original - + Handler = require '../commons/Handler' return @sendBadInputError(res) if not Handler.isID(purchasedOriginal) - + collection = purchased?.collection return @sendBadInputError(res) if not collection in @jsonSchema.properties.purchased.properties.collection.enum - + handler = require('../' + handlers[collection]) criteria = { 'original': purchasedOriginal } sort = { 'version.major': -1, 'version.minor': -1 } - + handler.modelClass.findOne(criteria).sort(sort).exec (err, purchasedItem) => gemsOwned = req.user.get('earned')?.gems or 0 return @sendDatabaseError(res, err) if err @@ -51,12 +49,13 @@ PurchaseHandler = class PurchaseHandler extends Handler if purchase @addPurchaseToUser(req, res) return @sendSuccess(res, @formatEntity(req, purchase)) - + else super(req, res) - + onPostSuccess: (req) -> @addPurchaseToUser(req) + req.user?.saveActiveUser 'purchase' addPurchaseToUser: (req) -> user = req.user @@ -77,10 +76,10 @@ PurchaseHandler = class PurchaseHandler extends Handler user.set('purchased', purchased) #- deduct the gems from the user - spent = hadSpent = user.get('spent') ? 0 + spent = user.get('spent') ? 0 spent += item.get('gems') user.set('spent', spent) - + user.save() - + module.exports = new PurchaseHandler() diff --git a/server/queues/scoring.coffee b/server/queues/scoring.coffee index 4a4c78687..ca6acfc01 100644 --- a/server/queues/scoring.coffee +++ b/server/queues/scoring.coffee @@ -15,6 +15,8 @@ bayes = new (require 'bayesian-battle')() scoringTaskQueue = undefined scoringTaskTimeoutInSeconds = 600 +SIMULATOR_VERSION = 3 + module.exports.setup = (app) -> connectToScoringQueue() connectToScoringQueue = -> @@ -35,7 +37,7 @@ module.exports.addPairwiseTaskToQueueFromRequest = (req, res) -> taskPair = req.body.sessions addPairwiseTaskToQueue req.body.sessions, (err, success) -> if err? then return errors.serverError res, "There was an error adding pairwise tasks: #{err}" - sendResponseObject req, res, {'message': 'All task pairs were succesfully sent to the queue'} + sendResponseObject req, res, {message: 'All task pairs were succesfully sent to the queue'} addPairwiseTaskToQueue = (taskPair, cb) -> LevelSession.findOne(_id: taskPair[0]).lean().exec (err, firstSession) => @@ -73,7 +75,7 @@ module.exports.resimulateAllSessions = (req, res) -> result = _.sample result, 10 async.each result, resimulateSession.bind(@, originalLevelID, levelMajorVersion), (err) -> if err? then return errors.serverError res, err - sendResponseObject req, res, {'message': 'All task pairs were succesfully sent to the queue'} + sendResponseObject req, res, {message: 'All task pairs were succesfully sent to the queue'} resimulateSession = (originalLevelID, levelMajorVersion, session, cb) => sessionUpdateObject = @@ -96,139 +98,99 @@ resimulateSession = (originalLevelID, levelMajorVersion, session, cb) => if taskPairError? then return cb taskPairError, null cb null -selectRandomSkipIndex = (numberOfSessions) -> - numbers = [0...numberOfSessions] - numberWeights = [] - lambda = 0.025 +earliestSubmissionCache = {} +findEarliestSubmission = (queryParams, callback) -> + cacheKey = JSON.stringify queryParams + return callback null, cached if cached = earliestSubmissionCache[cacheKey] + LevelSession.findOne(queryParams).sort(submitDate: 1).lean().exec (err, earliest) -> + return callback err if err + result = earliestSubmissionCache[cacheKey] = earliest?.submitDate + callback null, result - for number, index in numbers - numberWeights[index] = lambda*Math.exp(-1*lambda*number) + lambda/(numberOfSessions/15) - sum = numberWeights.reduce (a, b) -> a + b +findRecentRandomSession = (queryParams, callback) -> + # We pick a random submitDate between the first submit date for the level and now, then do a $lt fetch to find a session to simulate. + # We bias it towards recently submitted sessions. + findEarliestSubmission queryParams, (err, startDate) -> + return callback err, null unless startDate + now = new Date() + interval = now - startDate + cutoff = new Date now - Math.pow(Math.random(), 4) * interval + queryParams.submitDate = $gte: startDate, $lt: cutoff + selection = 'team totalScore transpiledCode submittedCodeLanguage teamSpells levelID creatorName creator submitDate' + LevelSession.findOne(queryParams).sort(submitDate: -1).select(selection).lean().exec (err, session) -> + return callback err if err + callback null, session - for number, index in numberWeights - numberWeights[index] /= sum +findRandomSession = (queryParams, callback) -> + queryParams.submitted = true + favorRecent = queryParams.favorRecent + delete queryParams.favorRecent + if favorRecent + return findRecentRandomSession queryParams, callback + queryParams.randomSimulationIndex = $lte: Math.random() + selection = 'team totalScore transpiledCode submittedCodeLanguage teamSpells levelID creatorName creator submitDate' + sort = randomSimulationIndex: -1 + LevelSession.findOne(queryParams).sort(sort).select(selection).lean().exec (err, session) -> + return callback err if err + return callback null, session if session + delete queryParams.randomSimulationIndex # Effectively switch to $gt, if our randomSimulationIndex was lower than the lowest one. + LevelSession.findOne(queryParams).sort(sort).select(selection).lean().exec (err, session) -> + return callback err if err + callback null, session - rand = (min, max) -> Math.random() * (max - min) + min - - totalWeight = 1 - randomNumber = Math.random() - weightSum = 0 - - for number, i in numbers - weightSum += numberWeights[i] - - if (randomNumber <= weightSum) - return numbers[i] +formatSessionInformation = (session) -> + sessionID: session._id + team: session.team ? 'No team' + transpiledCode: session.transpiledCode + submittedCodeLanguage: session.submittedCodeLanguage + teamSpells: session.teamSpells ? {} + levelID: session.levelID + creatorName: session.creatorName + creator: session.creator + totalScore: session.totalScore module.exports.getTwoGames = (req, res) -> - #if userIsAnonymous req then return errors.unauthorized(res, 'You need to be logged in to get games.') + #if isUserAnonymous req then return errors.unauthorized(res, 'You need to be logged in to get games.') humansGameID = req.body.humansGameID ogresGameID = req.body.ogresGameID - ladderGameIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span', 'dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove'] + return if simulatorIsTooOld req, res + #ladderGameIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span'] # Let's not give any extra simulations to old ladders. + ladderGameIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove', 'harrowland', 'zero-sum'] levelID = _.sample ladderGameIDs unless ogresGameID and humansGameID - #fetch random games here - queryParams = - 'levelID': levelID - 'submitted': true - 'team': 'humans' - selection = 'team totalScore transpiledCode submittedCodeLanguage teamSpells levelID creatorName creator submitDate' - LevelSession.count queryParams, (err, numberOfHumans) => - if err? then return errors.serverError(res, 'Couldn\'t get the number of human games') - unless numberOfHumans + recentHumans = Math.random() < 0.5 # We pick one session favoring recent submissions, then find another one uniformly to play against + async.map [{levelID: levelID, team: 'humans', favorRecent: recentHumans}, {levelID: levelID, team: 'ogres', favorRecent: not recentHumans}], findRandomSession, (err, sessions) -> + if err then return errors.serverError(res, "Couldn't get two games to simulate for #{levelID}.") + unless sessions.length is 2 res.send(204, 'No games to score.') return res.end() - humanSkipCount = Math.floor(Math.random() * numberOfHumans) - ogreCountParams = - 'levelID': levelID - 'submitted': true - 'team': 'ogres' - LevelSession.count ogreCountParams, (err, numberOfOgres) => - if err? then return errors.serverError(res, 'Couldn\'t get the number of ogre games') - unless numberOfOgres - res.send(204, 'No games to score.') - return res.end() - ogresSkipCount = Math.floor(Math.random() * numberOfOgres) - - query = LevelSession - .aggregate() - .match(queryParams) - .project(selection) - .sort({'submitDate': -1}) - .skip(humanSkipCount) - .limit(1) - query.exec (err, randomSession) => - if err? then return errors.serverError(res, "Couldn't select a random session! #{err}") - randomSession = randomSession[0] - queryParams = - 'levelID': levelID - 'submitted': true - 'team': 'ogres' - query = LevelSession - .aggregate() - .match(queryParams) - .project(selection) - .sort({'submitDate': -1}) - .skip(ogresSkipCount) - .limit(1) - query.exec (err, otherSession) => - if err? then return errors.serverError(res, 'Couldn\'t select the other random session!') - otherSession = otherSession[0] - taskObject = - 'messageGenerated': Date.now() - 'sessions': [] - for session in [randomSession, otherSession] - sessionInformation = - 'sessionID': session._id - 'team': session.team ? 'No team' - 'transpiledCode': session.transpiledCode - 'submittedCodeLanguage': session.submittedCodeLanguage - 'teamSpells': session.teamSpells ? {} - 'levelID': session.levelID - 'creatorName': session.creatorName - 'creator': session.creator - 'totalScore': session.totalScore - taskObject.sessions.push sessionInformation - #console.log 'Dispatching random game between', taskObject.sessions[0].creatorName, 'and', taskObject.sessions[1].creatorName - sendResponseObject req, res, taskObject + taskObject = messageGenerated: Date.now(), sessions: (formatSessionInformation session for session in sessions) + #console.log 'Dispatching random game between', taskObject.sessions[0].creatorName, 'and', taskObject.sessions[1].creatorName + sendResponseObject req, res, taskObject else #console.log "Directly simulating #{humansGameID} vs. #{ogresGameID}." LevelSession.findOne(_id: humansGameID).select(selection).lean().exec (err, humanSession) => if err? then return errors.serverError(res, 'Couldn\'t find the human game') LevelSession.findOne(_id: ogresGameID).select(selection).lean().exec (err, ogreSession) => if err? then return errors.serverError(res, 'Couldn\'t find the ogre game') - taskObject = - 'messageGenerated': Date.now() - 'sessions': [] - for session in [humanSession, ogreSession] - sessionInformation = - 'sessionID': session._id - 'team': session.team ? 'No team' - 'transpiledCode': session.transpiledCode - 'submittedCodeLanguage': session.submittedCodeLanguage - 'teamSpells': session.teamSpells ? {} - 'levelID': session.levelID - 'creatorName': session.creatorName - 'creator': session.creator - 'totalScore': session.totalScore - - taskObject.sessions.push sessionInformation + taskObject = messageGenerated: Date.now(), sessions: (formatSessionInformation session for session in [humanSession, ogreSession]) sendResponseObject req, res, taskObject module.exports.recordTwoGames = (req, res) -> sessions = req.body.sessions #console.log 'Recording non-chained result of', sessions?[0]?.name, sessions[0]?.metrics?.rank, 'and', sessions?[1]?.name, sessions?[1]?.metrics?.rank + return if simulatorIsTooOld req, res + req.body?.simulator?.user = '' + req.user?._id yetiGuru = clientResponseObject: req.body, isRandomMatch: true async.waterfall [ - fetchLevelSession.bind(yetiGuru) - updateSessions.bind(yetiGuru) - indexNewScoreArray.bind(yetiGuru) - addMatchToSessions.bind(yetiGuru) - updateUserSimulationCounts.bind(yetiGuru, req.user._id) + calculateSessionScores.bind(yetiGuru) # Fetches a few small properties from both sessions, prepares @levelSessionUpdates with the score part + indexNewScoreArray.bind(yetiGuru) # Creates and returns @newScoresObject, no query + addMatchToSessionsAndUpdate.bind(yetiGuru) # Adds matches to the session updates and does the writes + updateUserSimulationCounts.bind(yetiGuru, req.user?._id) ], (err, successMessageObject) -> - if err? then return errors.serverError res, "There was an error recording the single game:#{err}" - sendResponseObject req, res, {'message': 'The single game was submitted successfully!'} + if err? then return errors.serverError res, "There was an error recording the single game: #{err}" + sendResponseObject req, res, {message: 'The single game was submitted successfully!'} module.exports.createNewTask = (req, res) -> requestSessionID = req.body.session @@ -306,6 +268,7 @@ updateSessionToSubmit = (transpiledCode, sessionToUpdate, callback) -> numberOfWinsAndTies: 0 numberOfLosses: 0 isRanking: true + randomSimulationIndex: Math.random() LevelSession.update {_id: sessionToUpdate._id}, sessionUpdateObject, (err, result) -> callback err, sessionToUpdate @@ -316,8 +279,6 @@ fetchInitialSessionsToRankAgainst = (levelMajorVersion, levelID, submittedSessio 'level.original': levelID 'level.majorVersion': levelMajorVersion submitted: true - submittedCode: - $exists: true team: opposingTeam sortParameters = @@ -339,7 +300,7 @@ generateAndSendTaskPairsToTheQueue = (sessionToRankAgainst, submittedSession, ca if taskPairError? then return callback taskPairError #console.log 'Sent task pairs to the queue!' #console.log taskPairs - callback null, {'message': 'All task pairs were succesfully sent to the queue'} + callback null, {message: 'All task pairs were succesfully sent to the queue'} module.exports.dispatchTaskToConsumer = (req, res) -> yetiGuru = {} @@ -388,25 +349,7 @@ parseTaskQueueMessage = (message, cb) -> constructTaskObject = (taskMessageBody, message, callback) -> async.map taskMessageBody.sessions, getSessionInformation, (err, sessions) -> if err? then return callback err - - taskObject = - 'messageGenerated': Date.now() - 'sessions': [] - - for session in sessions - sessionInformation = - 'sessionID': session._id - 'submitDate': session.submitDate - 'team': session.team ? 'No team' - 'transpiledCode': session.transpiledCode - 'submittedCodeLanguage': session.submittedCodeLanguage - 'teamSpells': session.teamSpells ? {} - 'levelID': session.levelID - 'creator': session.creator - 'creatorName': session.creatorName - 'totalScore': session.totalScore - - taskObject.sessions.push sessionInformation + taskObject = messageGenerated: Date.now(), sessions: (formatSessionInformation session for session in sessions) callback null, taskObject, message constructTaskLogObject = (calculatorUserID, taskObject, message, callback) -> @@ -436,7 +379,9 @@ getSessionInformation = (sessionIDString, callback) -> callback null, session module.exports.processTaskResult = (req, res) -> + return if simulatorIsTooOld req, res originalSessionID = req.body?.originalSessionID + req.body?.simulator?.user = '' + req.user?._id yetiGuru = {} try async.waterfall [ @@ -447,10 +392,10 @@ module.exports.processTaskResult = (req, res) -> fetchLevelSession.bind(yetiGuru) checkSubmissionDate.bind(yetiGuru) logTaskComputation.bind(yetiGuru) - updateSessions.bind(yetiGuru) + calculateSessionScores.bind(yetiGuru) indexNewScoreArray.bind(yetiGuru) - addMatchToSessions.bind(yetiGuru) - updateUserSimulationCounts.bind(yetiGuru, req.user._id) + addMatchToSessionsAndUpdate.bind(yetiGuru) + updateUserSimulationCounts.bind(yetiGuru, req.user?._id) determineIfSessionShouldContinueAndUpdateLog.bind(yetiGuru) findNearestBetterSessionID.bind(yetiGuru) addNewSessionsToQueue.bind(yetiGuru) @@ -458,15 +403,15 @@ module.exports.processTaskResult = (req, res) -> if err is 'shouldn\'t continue' markSessionAsDoneRanking originalSessionID, (err) -> if err? then return sendResponseObject req, res, {'error': 'There was an error marking the session as done ranking'} - sendResponseObject req, res, {'message': 'The scores were updated successfully, person lost so no more games are being inserted!'} + sendResponseObject req, res, {message: 'The scores were updated successfully, person lost so no more games are being inserted!'} else if err is 'no session was found' markSessionAsDoneRanking originalSessionID, (err) -> if err? then return sendResponseObject req, res, {'error': 'There was an error marking the session as done ranking'} - sendResponseObject req, res, {'message': 'There were no more games to rank (game is at top)!'} + sendResponseObject req, res, {message: 'There were no more games to rank (game is at top)!'} else if err? errors.serverError res, "There was an error:#{err}" else - sendResponseObject req, res, {'message': 'The scores were updated successfully and more games were sent to the queue!'} + sendResponseObject req, res, {message: 'The scores were updated successfully and more games were sent to the queue!'} catch e errors.serverError res, 'There was an error processing the task result!' @@ -476,39 +421,26 @@ verifyClientResponse = (responseObject, callback) -> callback 'The response to that query is required to be a JSON object.' else @clientResponseObject = responseObject - - #log.info 'Verified client response!' callback null, responseObject fetchTaskLog = (responseObject, callback) -> - query = TaskLog.findOne _id: responseObject.taskID - query.exec (err, taskLog) => + TaskLog.findOne(_id: responseObject.taskID).lean().exec (err, taskLog) => return callback new Error("Couldn't find TaskLog for _id #{responseObject.taskID}!") unless taskLog @taskLog = taskLog - #log.info 'Fetched task log!' - callback err, taskLog.toObject() + callback err, taskLog checkTaskLog = (taskLog, callback) -> if taskLog.calculationTimeMS then return callback 'That computational task has already been performed' if hasTaskTimedOut taskLog.sentDate then return callback 'The task has timed out' - #log.info 'Checked task log' callback null deleteQueueMessage = (callback) -> scoringTaskQueue.deleteMessage @clientResponseObject.receiptHandle, (err) -> - #log.info 'Deleted queue message' callback err fetchLevelSession = (callback) -> - findParameters = - _id: @clientResponseObject.originalSessionID - - query = LevelSession - .findOne(findParameters) - .lean() - query.exec (err, session) => + LevelSession.findOne(_id: @clientResponseObject.originalSessionID).select('submitDate creator level standardDeviation meanStrength totalScore submittedCodeLanguage').lean().exec (err, session) => @levelSession = session - #log.info 'Fetched level session' callback err checkSubmissionDate = (callback) -> @@ -516,72 +448,60 @@ checkSubmissionDate = (callback) -> if Number(supposedSubmissionDate) isnt Number(@levelSession.submitDate) callback 'The game has been resubmitted. Removing from queue...' else - #log.info 'Checked submission date' callback null logTaskComputation = (callback) -> @taskLog.set('calculationTimeMS', @clientResponseObject.calculationTimeMS) - @taskLog.set('sessions') + @taskLog.set('sessions') # Huh? @taskLog.calculationTimeMS = @clientResponseObject.calculationTimeMS @taskLog.sessions = @clientResponseObject.sessions @taskLog.save (err, saved) -> - #log.info 'Logged task computation' callback err -updateSessions = (callback) -> +calculateSessionScores = (callback) -> sessionIDs = _.pluck @clientResponseObject.sessions, 'sessionID' - async.map sessionIDs, retrieveOldSessionData, (err, oldScores) => - if err? then callback err, {'error': 'There was an error retrieving the old scores'} + if err? then callback err, {error: 'There was an error retrieving the old scores'} try oldScoreArray = _.toArray putRankingFromMetricsIntoScoreObject @clientResponseObject, oldScores newScoreArray = bayes.updatePlayerSkills oldScoreArray - saveNewScoresToDatabase newScoreArray, callback + createSessionScoreUpdate.call @, scoreObject for scoreObject in newScoreArray + callback err, newScoreArray catch e callback e -saveNewScoresToDatabase = (newScoreArray, callback) -> - async.eachSeries newScoreArray, updateScoreInSession, (err) -> - #log.info 'Saved new scores to database' - callback err, newScoreArray - -updateScoreInSession = (scoreObject, callback) -> - LevelSession.findOne {'_id': scoreObject.id}, (err, session) -> - if err? then return callback err, null - - session = session.toObject() - newTotalScore = scoreObject.meanStrength - 1.8 * scoreObject.standardDeviation - scoreHistoryAddition = [Date.now(), newTotalScore] - updateObject = - meanStrength: scoreObject.meanStrength - standardDeviation: scoreObject.standardDeviation - totalScore: newTotalScore - $push: {scoreHistory: {$each: [scoreHistoryAddition], $slice: -1000}} - - LevelSession.update {'_id': scoreObject.id}, updateObject, callback - #log.info "New total score for session #{scoreObject.id} is #{updateObject.totalScore}" +createSessionScoreUpdate = (scoreObject) -> + newTotalScore = scoreObject.meanStrength - 1.8 * scoreObject.standardDeviation + scoreHistoryAddition = [Date.now(), newTotalScore] + @levelSessionUpdates ?= {} + @levelSessionUpdates[scoreObject.id] = + meanStrength: scoreObject.meanStrength + standardDeviation: scoreObject.standardDeviation + totalScore: newTotalScore + $push: {scoreHistory: {$each: [scoreHistoryAddition], $slice: -1000}} + randomSimulationIndex: Math.random() indexNewScoreArray = (newScoreArray, callback) -> newScoresObject = _.indexBy newScoreArray, 'id' @newScoresObject = newScoresObject callback null, newScoresObject -addMatchToSessions = (newScoreObject, callback) -> +addMatchToSessionsAndUpdate = (newScoreObject, callback) -> matchObject = {} matchObject.date = new Date() matchObject.opponents = {} for session in @clientResponseObject.sessions sessionID = session.sessionID - matchObject.opponents[sessionID] = {} - matchObject.opponents[sessionID].sessionID = sessionID - matchObject.opponents[sessionID].userID = session.creator - matchObject.opponents[sessionID].name = session.name - matchObject.opponents[sessionID].totalScore = session.totalScore - matchObject.opponents[sessionID].metrics = {} - matchObject.opponents[sessionID].metrics.rank = Number(newScoreObject[sessionID]?.gameRanking ? 0) - matchObject.opponents[sessionID].codeLanguage = newScoreObject[sessionID].submittedCodeLanguage + matchObject.opponents[sessionID] = match = {} + match.sessionID = sessionID + match.userID = session.creator + match.name = session.name + match.totalScore = session.totalScore + match.metrics = {} + match.metrics.rank = Number(newScoreObject[sessionID]?.gameRanking ? 0) + match.codeLanguage = newScoreObject[sessionID].submittedCodeLanguage - #log.info "Match object computed, result: #{matchObject}" + #log.info "Match object computed, result: #{JSON.stringify(matchObject, null, 2)}" #log.info 'Writing match object to database...' #use bind with async to do the writes sessionIDs = _.pluck @clientResponseObject.sessions, 'sessionID' @@ -597,13 +517,12 @@ updateMatchesInSession = (matchObject, sessionID, callback) -> opponentsArray = _.toArray opponentsClone currentMatchObject.opponents = opponentsArray currentMatchObject.codeLanguage = matchObject.opponents[opponentsArray[0].sessionID].codeLanguage - LevelSession.findOne {'_id': sessionID}, (err, session) -> - session = session.toObject() - currentMatchObject.playtime = session.playtime ? 0 - sessionUpdateObject = - $push: {matches: {$each: [currentMatchObject], $slice: -200}} - #log.info "Updating session #{sessionID}" - LevelSession.update {'_id': sessionID}, sessionUpdateObject, callback + #currentMatchObject.simulator = @clientResponseObject.simulator # Uncomment when actively debugging simulation mismatches + #currentMatchObject.randomSeed = parseInt(@clientResponseObject.randomSeed or 0, 10) # Uncomment when actively debugging simulation mismatches + sessionUpdateObject = @levelSessionUpdates[sessionID] + sessionUpdateObject.$push.matches = {$each: [currentMatchObject], $slice: -200} + #log.info "Update is #{JSON.stringify(sessionUpdateObject, null, 2)}" + LevelSession.update {_id: sessionID}, sessionUpdateObject, callback updateUserSimulationCounts = (reqUserID, callback) -> incrementUserSimulationCount reqUserID, 'simulatedBy', (err) => @@ -615,6 +534,7 @@ updateUserSimulationCounts = (reqUserID, callback) -> callback null incrementUserSimulationCount = (userID, type, callback) => + return callback null unless userID inc = {} inc[type] = 1 User.update {_id: userID}, {$inc: inc}, (err, affected) -> @@ -625,20 +545,16 @@ determineIfSessionShouldContinueAndUpdateLog = (cb) -> sessionID = @clientResponseObject.originalSessionID sessionRank = parseInt @clientResponseObject.originalSessionRank - queryParameters = - _id: sessionID - - updateParameters = - '$inc': {} + queryParameters = _id: sessionID + updateParameters = '$inc': {} if sessionRank is 0 updateParameters['$inc'] = {numberOfWinsAndTies: 1} else updateParameters['$inc'] = {numberOfLosses: 1} - LevelSession.findOneAndUpdate queryParameters, updateParameters, {select: 'numberOfWinsAndTies numberOfLosses'}, (err, updatedSession) -> + LevelSession.findOneAndUpdate queryParameters, updateParameters, {select: 'numberOfWinsAndTies numberOfLosses', lean: true}, (err, updatedSession) -> if err? then return cb err, updatedSession - updatedSession = updatedSession.toObject() totalNumberOfGamesPlayed = updatedSession.numberOfWinsAndTies + updatedSession.numberOfLosses if totalNumberOfGamesPlayed < 10 @@ -676,8 +592,6 @@ findNearestBetterSessionID = (cb) -> 'level.original': levelOriginalID 'level.majorVersion': levelMajorVersion submitted: true - submittedCode: - $exists: true team: opposingTeam if opponentSessionTotalScore < 30 @@ -693,10 +607,10 @@ findNearestBetterSessionID = (cb) -> selectString = '_id totalScore' query = LevelSession.findOne(queryParameters) - .sort(sortParameters) - .limit(limitNumber) - .select(selectString) - .lean() + .sort(sortParameters) + .limit(limitNumber) + .select(selectString) + .lean() #console.log "Finding session with score near #{opponentSessionTotalScore}" query.exec (err, session) -> @@ -706,9 +620,9 @@ findNearestBetterSessionID = (cb) -> cb err, session._id retrieveAllOpponentSessionIDs = (sessionID, cb) -> - query = LevelSession.findOne({'_id': sessionID}) - .select('matches.opponents.sessionID matches.date submitDate') - .lean() + query = LevelSession.findOne({_id: sessionID}) + .select('matches.opponents.sessionID matches.date submitDate') + .lean() query.exec (err, session) -> if err? then return cb err, null opponentSessionIDs = (match.opponents[0].sessionID for match in session.matches when match.date > session.submitDate) @@ -763,18 +677,30 @@ putRankingFromMetricsIntoScoreObject = (taskObject, scoreObject) -> return scoreObject retrieveOldSessionData = (sessionID, callback) -> - LevelSession.findOne {'_id': sessionID}, (err, session) -> - return callback err, {'error': 'There was an error retrieving the session.'} if err? + formatOldScoreObject = (session) -> + standardDeviation: session.standardDeviation ? 25/3 + meanStrength: session.meanStrength ? 25 + totalScore: session.totalScore ? (25 - 1.8*(25/3)) + id: sessionID + submittedCodeLanguage: session.submittedCodeLanguage - session = session.toObject() - oldScoreObject = - 'standardDeviation': session.standardDeviation ? 25/3 - 'meanStrength': session.meanStrength ? 25 - 'totalScore': session.totalScore ? (25 - 1.8*(25/3)) - 'id': sessionID - 'submittedCodeLanguage': session.submittedCodeLanguage - callback err, oldScoreObject + return formatOldScoreObject @levelSession if sessionID is @levelSession?._id # No need to fetch again + + query = _id: sessionID + selection = 'standardDeviation meanStrength totalScore submittedCodeLanguage' + LevelSession.findOne(query).select(selection).lean().exec (err, session) -> + return callback err, {'error': 'There was an error retrieving the session.'} if err? + callback err, formatOldScoreObject session markSessionAsDoneRanking = (sessionID, cb) -> #console.log 'Marking session as done ranking...' - LevelSession.update {'_id': sessionID}, {'isRanking': false}, cb + LevelSession.update {_id: sessionID}, {isRanking: false}, cb + +simulatorIsTooOld = (req, res) -> + clientSimulator = req.body.simulator + return false if clientSimulator?.version >= SIMULATOR_VERSION + message = "Old simulator version #{clientSimulator?.version}, need to clear cache and get version #{SIMULATOR_VERSION}." + log.debug "400: #{message}" + res.send 400, message + res.end() + true diff --git a/server/queues/task/ScoringTask.coffee b/server/queues/task/ScoringTask.coffee index b6dcf27d9..e24a56130 100644 --- a/server/queues/task/ScoringTask.coffee +++ b/server/queues/task/ScoringTask.coffee @@ -9,4 +9,6 @@ ScoringTaskSchema = new mongoose.Schema( sessions: {type: Array, default: []} ) +ScoringTaskSchema.index({createdAt: 1}, {expireAfterSeconds: 3600}) + module.exports = mongoose.model('scoringTask', ScoringTaskSchema) diff --git a/server/routes/auth.coffee b/server/routes/auth.coffee index fee2f2644..07392ae40 100644 --- a/server/routes/auth.coffee +++ b/server/routes/auth.coffee @@ -5,8 +5,9 @@ UserHandler = require '../users/user_handler' LevelSession = require '../levels/sessions/LevelSession' config = require '../../server_config' errors = require '../commons/errors' -mail = require '../commons/mail' languages = require '../routes/languages' +sendwithus = require '../sendwithus' +log = require 'winston' module.exports.setup = (app) -> authentication.serializeUser((user, done) -> done(null, user._id)) @@ -15,13 +16,13 @@ module.exports.setup = (app) -> authentication.use(new LocalStrategy( (username, password, done) -> - + # kind of a hacky way to make it possible for iPads to 'log in' with their unique device id if username.length is 36 and '@' not in username # must be an identifier for vendor q = { iosIdentifierForVendor: username } else q = { emailLower: username.toLowerCase() } - + User.findOne(q).exec((err, user) -> return done(err) if err return done(null, false, {message: 'not found', property: 'email'}) if not user @@ -32,7 +33,7 @@ module.exports.setup = (app) -> hash = User.hashPassword(password) unless user.get('passwordHash') is hash - return done(null, false, {message: 'is wrong.', property: 'password'}) + return done(null, false, {message: 'is wrong', property: 'password'}) return done(null, user) ) )) @@ -98,16 +99,23 @@ module.exports.setup = (app) -> User.findOne({emailLower: req.body.email.toLowerCase()}).exec((err, user) -> if not user - return errors.notFound(res, [{message: 'not found.', property: 'email'}]) + return errors.notFound(res, [{message: 'not found', property: 'email'}]) user.set('passwordReset', Math.random().toString(36).slice(2, 7).toUpperCase()) user.save (err) => return errors.serverError(res) if err - if config.isProduction - options = createMailOptions req.body.email, user.get('passwordReset') - mail.transport.sendMail options, (error, response) -> - if error - console.error "Error sending mail: #{error.message or error}" + unless config.unittest + context = + email_id: sendwithus.templates.generic_email + recipient: + address: req.body.email + email_data: + subject: 'CodeCombat Recovery Password' + title: 'Recovery Password' + content: "<p>Your CodeCombat recovery password for email #{req.body.email} is: #{user.get('passwordReset')}</p><p>Log in at <a href=\"http://codecombat.com/account/settings\">http://codecombat.com/account/settings</a> and change it.</p><p>Hope this helps!</p>" + sendwithus.api.send context, (err, result) -> + if err + console.error "Error sending password reset email: #{err.message or err}" return errors.serverError(res) if err else return res.end() @@ -131,7 +139,7 @@ module.exports.setup = (app) -> session.set 'unsubscribed', true session.save (err) -> return errors.serverError res, 'Database failure.' if err - res.send "Unsubscribed #{req.query.email} from CodeCombat emails for #{session.levelName} #{session.team} ladder updates. Sorry to see you go! <p><a href='/play/ladder/#{session.levelID}#my-matches'>Ladder preferences</a></p>" + res.send "Unsubscribed #{req.query.email} from CodeCombat emails for #{session.get('levelName')} #{session.get('team')} ladder updates. Sorry to see you go! <p><a href='/play/ladder/#{session.levelID}#my-matches'>Ladder preferences</a></p>" res.end() User.findOne({emailLower: req.query.email.toLowerCase()}).exec (err, user) -> @@ -191,16 +199,10 @@ module.exports.loginUser = loginUser = (req, res, user, send=true, next=null) -> module.exports.makeNewUser = makeNewUser = (req) -> user = new User({anonymous: true}) - user.set 'testGroupNumber', Math.floor(Math.random() * 256) # also in app/lib/auth + user.set 'testGroupNumber', Math.floor(Math.random() * 256) # also in app/core/auth lang = languages.languageCodeFromAcceptedLanguages req.acceptedLanguages user.set 'preferredLanguage', lang if lang[...2] isnt 'en' - user.set 'lastIP', req.connection.remoteAddress - -createMailOptions = (receiver, password) -> - # TODO: use email templates here - options = - from: config.mail.username - to: receiver - replyTo: config.mail.username - subject: '[CodeCombat] Password Reset' - text: "You can log into your account with: #{password}" + user.set 'lastIP', (req.headers['x-forwarded-for'] or req.connection.remoteAddress)?.split(' ')[0] + user.set 'chinaVersion', true if req.chinaVersion + log.info "making new user #{user.get('_id')} with language #{user.get('preferredLanguage')} of #{req.acceptedLanguages} and chinaVersion #{req.chinaVersion} on #{if config.tokyo then 'Tokyo' else 'US'} server and lastIP #{req.connection.remoteAddress}." + user diff --git a/server/routes/contact.coffee b/server/routes/contact.coffee index 7f4991e19..d12fd052a 100644 --- a/server/routes/contact.coffee +++ b/server/routes/contact.coffee @@ -1,38 +1,87 @@ config = require '../../server_config' log = require 'winston' -mail = require '../commons/mail' User = require '../users/User' +sendwithus = require '../sendwithus' +async = require 'async' +LevelSession = require '../levels/sessions/LevelSession' +moment = require 'moment' +hipchat = require '../hipchat' module.exports.setup = (app) -> app.post '/contact', (req, res) -> return res.end() unless req.user - log.info "Sending mail from #{req.body.email} saying #{req.body.message}" - if config.isProduction - createMailOptions req.body.email, req.body.message, req.user, req.body.recipientID, req.body.subject, (options) -> - mail.transport.sendMail options, (error, response) -> - if error - log.error "Error sending mail: #{error.message or error}" - else - log.info "Mail sent successfully. Response: #{response.message}" + #log.info "Sending mail from #{req.body.email} saying #{req.body.message}" + createMailContext req, (context) -> + sendwithus.api.send context, (err, result) -> + if err + log.error "Error sending contact form email: #{err.message or err}" return res.end() -createMailOptions = (sender, message, user, recipientID, subject, done) -> - # TODO: use email templates here - options = - from: config.mail.username - to: config.mail.username - replyTo: sender - subject: "[CodeCombat] #{subject ? ('Feedback - ' + sender)}" - text: "#{message}\n\nUsername: #{user.get('name') or 'Anonymous'}\nID: #{user._id}" - #html: message.replace '\n', '<br>\n' +createMailContext = (req, done) -> + sender = req.body.sender or req.body.email + message = req.body.message + user = req.user + recipientID = req.body.recipientID + subject = req.body.subject + + level = if user?.get('points') > 0 then Math.floor(5 * Math.log((1 / 100) * (user.get('points') + 100))) + 1 else 0 + premium = user?.isPremium() + content = """ + #{message} + + -- + <a href='http://codecombat.com/user/#{user.get('slug') or user.get('_id')}'>#{user.get('name') or 'Anonymous'}</a> - Level #{level}#{if premium then ' - Subscriber' else ''} + """ + if req.body.browser + content += "\n#{req.body.browser} - #{req.body.screenSize}" + + context = + email_id: sendwithus.templates.plain_text_email + recipient: + address: if premium then config.mail.supportPremium else config.mail.supportPrimary + sender: + address: config.mail.username + reply_to: sender or user.get('email') + name: user.get('name') + email_data: + subject: "[CodeCombat] #{subject ? ('Feedback - ' + (sender or user.get('email')))}" + content: content if recipientID and (user.isAdmin() or ('employer' in (user.get('permissions') ? []))) User.findById(recipientID, 'email').exec (err, document) -> if err log.error "Error looking up recipient to email from #{recipientID}: #{err}" if err else - options.bcc = [options.to, sender] - options.to = document.get('email') - done options + context.recipient.bcc = [context.recipient.address, sender] + context.recipient.address = document.get('email') + context.email_data.content = message + done context else - done options + async.waterfall [ + fetchRecentSessions.bind undefined, user, context + # Can add other data-grabbing stuff here if we want. + ], (err, results) -> + console.error "Error getting contact message context for #{sender}: #{err}" if err + if req.body.screenshotURL + context.email_data.content += "\n<img src='#{req.body.screenshotURL}' />" + done context + + # I'll try having it just send the emails instead of spamming the chat. + #if /Level Load Error/.test context.email_data.subject + # message = "#{user.get('name') or user.get('email')} saw #{context.email_data.subject} <a href=\"http://direct.codecombat.com/editor/level/#{req.body.levelSlug}\">(level editor)</a>" + # hipchat.sendHipChatMessage message, ['tower'], color: 'red' + + +fetchRecentSessions = (user, context, callback) -> + query = creator: user.get('_id') + '' + projection = levelID: 1, levelName: 1, changed: 1, team: 1, codeLanguage: 1, 'state.complete': 1, playtime: 1 + sort = changed: -1 + LevelSession.find(query).select(projection).sort(sort).limit(3).lean().exec (err, sessions) -> + return callback err if err + for s in sessions + if s.playtime < 120 then playtime = "#{s.playtime}s played" + else if s.playtime < 7200 then playtime = "#{Math.round(s.playtime / 60)}m played" + else playtime = "#{Math.round(s.playtime / 3600)}h played" + ago = moment(s.changed).fromNow() + context.email_data.content += "\n<a href='http://codecombat.com/play/level/#{s.levelID}?session=#{s._id}&team=#{s.team or 'humans'}&dev=true'>#{s.levelName}#{if s.team is 'ogres' then ' ' + s.team else ''}</a>#{if s.state?.complete then ' complete ' else ''}- #{s.codeLanguage}, #{playtime}, #{ago}" + callback null diff --git a/server/routes/db.coffee b/server/routes/db.coffee index 9d7a0b5dc..d34dc6869 100644 --- a/server/routes/db.coffee +++ b/server/routes/db.coffee @@ -7,7 +7,7 @@ hipchat = require '../hipchat' module.exports.setup = (app) -> # This is hacky and should probably get moved somewhere else, I dunno app.get '/db/cla.submissions', (req, res) -> - return errors.unauthorized(res, 'You must be an admin to view that information') unless req.user?.isAdmin() + return errors.unauthorized(res, 'You must be an admin to view that information') unless req.user?.isAdmin() or ('github' in (req.user?.get('permissions') ? [])) res.setHeader('Content-Type', 'application/json') collection = mongoose.connection.db.collection 'cla.submissions', (err, collection) -> return log.error "Couldn't fetch CLA submissions because #{err}" if err @@ -26,7 +26,7 @@ module.exports.setup = (app) -> parts = module.split('/') module = parts[0] return getSchema(req, res, module) if parts[1] is 'schema' - if (not req.user) and req.route.method isnt 'get' + if (not req.user) and req.route.method isnt 'get' return errors.unauthorized(res, 'Must have an identity to do anything with the db. Do you have cookies enabled?') try @@ -43,10 +43,15 @@ module.exports.setup = (app) -> handler[req.route.method](req, res, parts[1..]...) catch error errorMessage = "Error trying db method #{req?.route?.method} route #{parts} from #{name}: #{error}" + if req.user? + userInfo = req.user.getUserInfo() + errorMessage += "\n-- User Info Id: #{userInfo.id} #{if req.user.isAnonymous() then '' else 'Email:'} #{userInfo.email}" log.error(errorMessage) log.error(error) log.error(error.stack) - hipchat.sendTowerHipChatMessage errorMessage + # TODO: Generally ignore this error: error: Error trying db method get route analytics.log.event from undefined: Error: Cannot find module '../undefined' + unless "#{parts}" in ['analytics.users.active'] + hipchat.sendHipChatMessage errorMessage, ['tower'], papertrail: true errors.notFound(res, "Route #{req?.path} not found.") getSchema = (req, res, moduleName) -> diff --git a/server/routes/file.coffee b/server/routes/file.coffee index 681c7715b..4c6de3cf0 100644 --- a/server/routes/file.coffee +++ b/server/routes/file.coffee @@ -9,24 +9,24 @@ module.exports.setup = (app) -> app.all '/file*', (req, res) -> return fileGet(req, res) if req.route.method is 'get' return filePost(req, res) if req.route.method is 'post' - return errors.badMethod(res, ['GET', 'POST']) + return fileDelete(req, res) if req.route.method is 'delete' + return errors.badMethod(res, ['GET', 'POST', 'DELETE']) + +fileDelete = (req, res) -> + return errors.forbidden(res) unless req.user + query = parsePathIntoQuery(req.path) + return errors.badInput(res) if not query.filename + Grid.gfs.collection('media').findOne query, (err, filedata) => + return errors.notFound(res) if not filedata + return errors.forbidden(res) unless userCanEditFile(req.user, filedata) + Grid.gfs.remove {_id: filedata._id, root: 'media'}, (err) -> + return errors.serverError(res) if err + return res.end() fileGet = (req, res) -> - path = req.path[6..] - path = decodeURI path - isFolder = false - try - objectId = mongoose.Types.ObjectId(path) - query = objectId - catch e - path = path.split('/') - filename = path[path.length-1] - path = path[...path.length-1].join('/') - query = - 'metadata.path': path - if filename then query.filename = filename else isFolder = true + query = parsePathIntoQuery(req.path) - if isFolder + if not query.filename # it's a folder, return folder contents Grid.gfs.collection('media').find query, (err, cursor) -> return errors.serverError(res) if err results = cursor.toArray (err, results) -> @@ -35,7 +35,7 @@ fileGet = (req, res) -> res.send(results) res.end() - else + else # it's a single file Grid.gfs.collection('media').findOne query, (err, filedata) => return errors.notFound(res) if not filedata readstream = Grid.gfs.createReadStream({_id: filedata._id, root: 'media'}) @@ -49,6 +49,22 @@ fileGet = (req, res) -> readstream.pipe(res) handleStreamEnd(res, res) +parsePathIntoQuery = (path) -> + path = path[6..] + path = decodeURI path + try + objectId = mongoose.Types.ObjectId(path) + query = objectId + catch e + path = path.split('/') + filename = path[path.length-1] + path = path[...path.length-1].join('/') + query = + 'metadata.path': path + query.filename = filename if filename + + query + postFileSchema = type: 'object' properties: @@ -114,7 +130,7 @@ savePNG = (req, res) -> userCanEditFile = (user=null, file=null) -> # no user means 'anyone'. No file means 'any file' return false unless user - return true if user.isAdmin() + return true if user.isAdmin() or user.isArtisan() return false unless file return true if file.metadata.creator is user.id return false diff --git a/server/routes/languages.coffee b/server/routes/languages.coffee index e6f411a04..c28a27c04 100644 --- a/server/routes/languages.coffee +++ b/server/routes/languages.coffee @@ -5,7 +5,7 @@ locale = require '../../app/locale/locale' # requiring from app; will break if module.exports.setup = (app) -> app.all '/languages/add/:lang/:namespace', (req, res) -> # Should probably store these somewhere - log.info "#{req.params.lang}.#{req.params.namespace} missing an i18n key:", req.body + #log.info "#{req.params.lang}.#{req.params.namespace} missing an i18n key:", req.body res.send('') res.end() diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index 3a1a60405..2173b78cd 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -15,6 +15,7 @@ if config.isProduction and config.redis.host isnt 'localhost' module.exports.setup = (app) -> app.all config.mail.mailchimpWebhook, handleMailchimpWebHook app.get '/mail/cron/ladder-update', handleLadderUpdate + app.get '/mail/cron/next-steps', handleNextSteps if lockManager setupScheduledEmails() @@ -58,8 +59,6 @@ candidateUpdateProfileTask = -> async.each timeRanges, emailTimeRange.bind({mailTaskName: mailTaskName}), (err) -> if err log.error "There was an error sending the candidate profile update reminder emails: #{err}" - else - log.info "Completed mail task #{mailTaskName}" lockManager.releaseLock mailTaskName, (err) -> if err? then return log.error "There was an error releasing the distributed lock for task #{mailTaskName}: #{err}" @@ -126,7 +125,7 @@ sendReminderEmailToCandidate = (candidate, sendEmailCallback) -> company_name: "CodeCombat" user_profile: "http://codecombat.com/account/profile/#{candidate._id}" recipient_address: encodeURIComponent(candidate.email) - log.info "Sending #{@timeRange.name} update reminder to #{context.recipient.name}(#{context.recipient.address})" + #log.info "Sending #{@timeRange.name} update reminder to #{context.recipient.name}(#{context.recipient.address})" newSentMail = mailTask: @mailTaskName user: candidate._id @@ -156,8 +155,6 @@ unapprovedCandidateFinishProfileTask = -> async.each timeRanges, emailUnapprovedCandidateTimeRange.bind({mailTaskName: mailTaskName}), (err) -> if err log.error "There was an error sending the candidate profile update reminder emails: #{err}" - else - log.info "Completed mail task #{mailTaskName}" lockManager.releaseLock mailTaskName, (err) -> if err? then return log.error "There was an error releasing the distributed lock for task #{mailTaskName}: #{err}" @@ -221,7 +218,7 @@ sendReminderEmailToUnapprovedCandidate = (candidate, sendEmailCallback) -> email_data: user_profile: "http://codecombat.com/account/profile/#{candidate._id}" recipient_address: encodeURIComponent(candidate.email) - log.info "Sending #{@timeRange.name} finish profile reminder to #{context.recipient.name}(#{context.recipient.address})" + #log.info "Sending #{@timeRange.name} finish profile reminder to #{context.recipient.name}(#{context.recipient.address})" newSentMail = mailTask: @mailTaskName user: candidate._id @@ -244,8 +241,6 @@ internalCandidateUpdateTask = -> emailInternalCandidateUpdateReminder.call {"mailTaskName":mailTaskName}, (err) -> if err log.error "There was an error sending the internal candidate update reminder.: #{err}" - else - log.info "Sent internal candidate update reminder email!" lockManager.releaseLock mailTaskName, (err) -> if err? then return log.error "There was an error releasing the distributed lock for task #{mailTaskName}: #{err}" @@ -293,7 +288,7 @@ sendInternalCandidateUpdateReminder = (candidate, cb) -> name: "The CodeCombat Team" email_data: new_candidate_profile: "http://codecombat.com/account/profile/#{candidate._id}" - log.info "Sending candidate updated reminder for #{candidate.jobProfile.name}" + #log.info "Sending candidate updated reminder for #{candidate.jobProfile.name}" newSentMail = mailTask: @mailTaskName user: candidate._id @@ -316,8 +311,6 @@ employerNewCandidatesAvailableTask = -> emailEmployerNewCandidatesAvailable.call {"mailTaskName":mailTaskName}, (err) -> if err log.error "There was an error completing the new candidates available task: #{err}" - else - log.info "Completed the employer new candidates available task!" lockManager.releaseLock mailTaskName, (err) -> if err? then return log.error "There was an error releasing the distributed lock for task #{mailTaskName}: #{err}" @@ -418,8 +411,6 @@ emailUserRemarkTaskRemindersTask = -> emailUserRemarkTaskReminders.call {"mailTaskName":mailTaskName}, (err) -> if err log.error "There was an error completing the #{mailTaskName}: #{err}" - else - log.info "Completed the #{mailTaskName}" lockManager.releaseLock mailTaskName, (err) -> if err? then return log.error "There was an error releasing the distributed lock for task #{mailTaskName}: #{err}" @@ -492,7 +483,7 @@ sendUserRemarkTaskEmail = (task, cb) -> candidate_name: user.jobProfile?.name ? "(Name not listed in job profile)" candidate_link: "http://codecombat.com/account/profile/#{task.user}" due_date: task.date - log.info "Sending recruitment task reminder to #{contact.email}" + #log.info "Sending recruitment task reminder to #{contact.email}" newSentMail = mailTask: mailTaskName user: task.contact @@ -527,8 +518,8 @@ employerMatchingCandidateNotificationTask = -> lockManager.setLock mailTaskName, lockDurationMs, (err, lockResult) -> ### ### End Employer Matching Candidate Notification Email ### -### Ladder Update Email ### ### Employer ignore ### + DEBUGGING = false LADDER_PREGAME_INTERVAL = 2 * 3600 * 1000 # Send emails two hours before players last submitted. getTimeFromDaysAgo = (now, daysAgo) -> @@ -544,17 +535,15 @@ isRequestFromDesignatedCronHandler = (req, res) -> return false return true - +### Ladder Update Email ### handleLadderUpdate = (req, res) -> - log.info('Going to see about sending ladder update emails.') - requestIsFromDesignatedCronHandler = DEBUGGING or isRequestFromDesignatedCronHandler req, res - return unless requestIsFromDesignatedCronHandler - + return unless DEBUGGING or isRequestFromDesignatedCronHandler req, res res.send('Great work, Captain Cron! I can take it from here.') res.end() # TODO: somehow fetch the histograms - emailDays = [1, 2, 4, 7, 14, 30] + #emailDays = [1, 2, 4, 7, 14, 30] + emailDays = [1, 3, 7] # Reduced to keep smaller monthly recipient footprint now = new Date() for daysAgo in emailDays # Get every session that was submitted in a 5-minute window after the time. @@ -564,7 +553,7 @@ handleLadderUpdate = (req, res) -> endTime = startTime + 15 * 60 * 1000 # Debugging: make sure there's something to send findParameters = {submitted: true, submitDate: {$gt: new Date(startTime), $lte: new Date(endTime)}} # TODO: think about putting screenshots in the email - selectString = 'creator team levelName levelID totalScore matches submitted submitDate scoreHistory level.original' + selectString = 'creator team levelName levelID totalScore matches submitted submitDate scoreHistory level.original unsubscribed' query = LevelSession.find(findParameters) .select(selectString) .lean() @@ -573,7 +562,7 @@ handleLadderUpdate = (req, res) -> if err log.error "Couldn't fetch ladder updates for #{findParameters}\nError: #{err}" return errors.serverError res, "Ladder update email query failed: #{JSON.stringify(err)}" - log.info "Found #{results.length} ladder sessions to email updates about for #{daysAgo} day(s) ago." + #log.info "Found #{results.length} ladder sessions to email updates about for #{daysAgo} day(s) ago." sendLadderUpdateEmail result, now, daysAgo for result in results sendLadderUpdateEmail = (session, now, daysAgo) -> @@ -583,10 +572,10 @@ sendLadderUpdateEmail = (session, now, daysAgo) -> return allowNotes = user.isEmailSubscriptionEnabled 'anyNotes' unless user.get('email') and allowNotes and not session.unsubscribed - log.info "Not sending email to #{user.get('email')} #{user.get('name')} because they only want emails about #{user.get('emailSubscriptions')}, #{user.get('emails')} - session unsubscribed: #{session.unsubscribed}" + #log.info "Not sending email to #{user.get('email')} #{user.get('name')} because they only want emails about #{user.get('emailSubscriptions')}, #{user.get('emails')} - session unsubscribed: #{session.unsubscribed}" return - unless session.levelName - log.info "Not sending email to #{user.get('email')} #{user.get('name')} because the session had no levelName in it." + unless session.levelName and session.team + #log.info "Not sending email to #{user.get('email')} #{user.get('name')} because the session had levelName #{session.levelName} or team #{session.team} in it." return name = if user.get('firstName') and user.get('lastName') then "#{user.get('firstName')}" else user.get('name') name = 'Wizard' if not name or name is 'Anoner' @@ -622,12 +611,12 @@ sendLadderUpdateEmail = (session, now, daysAgo) -> defeat: defeatContext victory: victoryContext levelVersions: levelVersionsContext - log.info "Sending ladder update email to #{context.recipient.address} with #{context.email_data.wins} wins and #{context.email_data.losses} losses since #{daysAgo} day(s) ago." + #log.info "Sending ladder update email to #{context.recipient.address} with #{context.email_data.wins} wins and #{context.email_data.losses} losses since #{daysAgo} day(s) ago." sendwithus.api.send context, (err, result) -> log.error "Error sending ladder update email: #{err} with result #{result}" if err urlForMatch = (match) -> - "http://codecombat.com/play/level/#{session.levelID}?team=#{session.team}&session=#{session._id}&opponent=#{match.opponents[0].sessionID}" + "http://codecombat.com/play/level/#{session.levelID}?team=#{session.team}&opponent=#{match.opponents[0].sessionID}" onFetchedDefeatedOpponent = (err, defeatedOpponent) -> if err @@ -673,6 +662,92 @@ getScoreHistoryGraphURL = (session, daysAgo) -> "https://chart.googleapis.com/chart?chs=600x75&cht=lxy&chtt=Score%3A+#{currentScore}&chts=222222,12,r&chf=a,s,000000FF&chls=2&chd=t:#{chartData}&chxt=y&chxr=0,#{minScore},#{maxScore}" ### End Ladder Update Email ### + +### Next Steps Email ### + +handleNextSteps = (req, res) -> + return unless DEBUGGING or isRequestFromDesignatedCronHandler req, res + res.send('Great work, Captain Cron! I can take it from here.') + res.end() + emailDays = [1] + now = new Date() + for daysAgo in emailDays + # Get every User that was created in a 5-minute window after the time. + startTime = getTimeFromDaysAgo now, daysAgo + endTime = startTime + 5 * 60 * 1000 + findParameters = {dateCreated: {$gt: new Date(startTime), $lte: new Date(endTime)}, emailLower: {$exists: true}} + selectString = 'name firstName lastName lastLevel points email gender emailSubscriptions emails dateCreated preferredLanguage aceConfig.language activity stats earned testGroupNumber ageRange' + query = User.find(findParameters).select(selectString) + do (daysAgo) -> + query.exec (err, results) -> + if err + log.error "Couldn't fetch next steps users for #{findParameters}\nError: #{err}" + return errors.serverError res, "Next steps email query failed: #{JSON.stringify(err)}" + log.info "Found #{results.length} next-steps users to email updates about for #{daysAgo} day(s) ago." if DEBUGGING + sendNextStepsEmail result, now, daysAgo for result in results + +sendNextStepsEmail = (user, now, daysAgo) -> + unless user.isEmailSubscriptionEnabled('generalNews') and user.isEmailSubscriptionEnabled('anyNotes') + log.info "Not sending email to #{user.get('email')} #{user.get('name')} because they only want emails about #{JSON.stringify(user.get('emails'))}" if DEBUGGING + return + + LevelSession.find({creator: user.get('_id') + ''}).select('levelName levelID changed state.complete playtime').lean().exec (err, sessions) -> + return log.error "Couldn't find sessions for #{user.get('email')}: #{err}" if err + complete = (s for s in sessions when s.state?.complete) + incomplete = (s for s in sessions when not s.state?.complete) + return if complete.length < 2 + + # TODO: find the next level to do somehow, for real + if incomplete.length + nextLevel = name: incomplete[0].levelName, slug: incomplete[0].levelID + else + nextLevel = null + err = null + do (err, nextLevel) -> + return log.error "Couldn't find next level for #{user.get('email')}: #{err}" if err + name = if user.get('firstName') and user.get('lastName') then "#{user.get('firstName')}" else user.get('name') + name = 'hero' if not name or name is 'Anoner' + #secretLevel = switch user.get('testGroupNumber') % 8 + # when 0, 1, 2, 3 then name: 'Forgetful Gemsmith', slug: 'forgetful-gemsmith' + # when 4, 5, 6, 7 then name: 'Signs and Portents', slug: 'signs-and-portents' + secretLevel = name: 'Signs and Portents', slug: 'signs-and-portents' # We turned off this test for now and are sending everyone to forgetful-gemsmith + + # TODO: make this smarter, actually data-driven, looking at all available sessions + shadowGuardSession = _.find sessions, levelID: 'shadow-guard' + isFast = shadowGuardSession and shadowGuardSession.playtime < 90 # Average is 107s + isVeryFast = shadowGuardSession and shadowGuardSession.playtime < 75 + isAdult = user.get('ageRange') in ['18-24', '25-34', '35-44', '45-100'] + isKid = not isAdult # Assume kid if not specified + offers = + 'app-academy': isAdult and isVeryFast + 'viking': isAdult and isFast + nAdditionalOffers = Math.max 0, 1 - _.filter(offers).length + possibleAdditionalOffers = ['bloc', 'tuts-plus', 'thinkful'] + for offer in _.sample possibleAdditionalOffers, nAdditionalOffers + offers[offer] = true + if user.isPremium() + offers = null + # TODO: do something with the preferredLanguage? + context = + email_id: sendwithus.templates.next_steps_email + recipient: + address: if DEBUGGING then 'nick@codecombat.com' else user.get('email') + name: name + email_data: + name: name + days_ago: daysAgo + nextLevelName: nextLevel?.name + nextLevelLink: if nextLevel then "http://codecombat.com/play/level/#{nextLevel.slug}" else null + secretLevelName: secretLevel.name + secretLevelLink: "http://codecombat.com/play/level/#{secretLevel.slug}" + levelsComplete: complete.length + offers: offers + log.info "Sending next steps email to #{context.recipient.address} with #{context.email_data.nextLevelName} next and #{context.email_data.levelsComplete} levels complete since #{daysAgo} day(s) ago." if DEBUGGING + sendwithus.api.send context, (err, result) -> + log.error "Error sending next steps email: #{err} with result #{result}" if err + +### End Next Steps Email ### + handleMailchimpWebHook = (req, res) -> post = req.body diff --git a/server/routes/stripe.coffee b/server/routes/stripe.coffee new file mode 100644 index 000000000..0e46de9c6 --- /dev/null +++ b/server/routes/stripe.coffee @@ -0,0 +1,267 @@ +async = require 'async' +config = require '../../server_config' +stripe = require('stripe')(config.stripe.secretKey) +User = require '../users/User' +Payment = require '../payments/Payment' +errors = require '../commons/errors' +mongoose = require 'mongoose' +utils = require '../../app/core/utils' + +module.exports.setup = (app) -> + # Cache customer -> user ID map (increases test perf considerably) + customerUserMap = {} + + logStripeWebhookError = (msg) -> + console.warn "Stripe Webhook Error: #{msg}" + + app.post '/stripe/webhook', (req, res) -> + + # Subscription renewal events: + # https://support.stripe.com/questions/what-events-can-i-see-when-a-subscription-is-renewed + + if req.body.type is 'invoice.payment_succeeded' + return handlePaymentSucceeded req, res + else if req.body.type is 'customer.subscription.deleted' + return handleSubscriptionDeleted req, res + else # ignore all other notifications + return res.send(200, '') + + app.get '/stripe/coupons', (req, res) -> + return errors.forbidden(res) unless req.user?.isAdmin() + stripe.coupons.list {limit: 100}, (err, coupons) -> + return errors.serverError(res) if err + res.send(200, coupons.data) + return res.end() + + handlePaymentSucceeded = (req, res) -> + # if they actually paid, give em some gems + + getUserID = (customerID, done) => + # Asumming Stripe customer never has a different userID + return done(null, customerUserMap[customerID]) if customerID of customerUserMap + stripe.customers.retrieve customerID, (err, customer) => + return done(err) if err + customerUserMap[customerID] = customer.metadata.id + return done(null, customerUserMap[customerID]) + + invoiceID = req.body.data.object.id + stripe.invoices.retrieve invoiceID, (err, invoice) => + return res.send(500, '') if err + unless invoice.total or invoice.discount?.coupon?.id is 'free' + # invoices made when trialing, probably given for people who resubscribe after unsubscribing + return res.send(200, '') + return res.send(200, '') unless invoice.lines?.data?.length > 0 + + getUserID invoice.customer, (err, userID) => + return res.send(500, '') if err + + # User is recipient if no metadata.id + recipientID = invoice.lines.data[0].metadata?.id or userID + + # Subscription id location depends on invoice line_item type + subscriptionID = invoice.lines.data[0].subscription or invoice.lines.data[0].id + + User.findById recipientID, (err, recipient) => + return res.send(500, '') if err + return res.send(200) unless recipient # just for the sake of testing... + + Payment.findOne {'stripe.invoiceID': invoiceID}, (err, payment) => + return res.send(200, '') if payment + payment = new Payment({ + 'purchaser': mongoose.Types.ObjectId(userID) + 'recipient': recipient._id + 'created': new Date().toISOString() + 'service': 'stripe' + 'amount': invoice.total + 'stripe': { + customerID: invoice.customer + invoiceID: invoice.id + subscriptionID: subscriptionID + } + }) + payment.set 'gems', 3500 if invoice.lines.data[0].plan?.id is 'basic' + + payment.save (err) => + return res.send(500, '') if err + return res.send(201, '') if invoice.lines.data[0].plan?.id isnt 'basic' + + # Update purchased gems + # TODO: is this correct for a resub? + Payment.find({recipient: recipient._id, gems: {$exists: true}}).select('gems').exec (err, payments) -> + gems = _.reduce payments, ((sum, p) -> sum + p.get('gems')), 0 + purchased = _.clone(recipient.get('purchased')) + purchased ?= {} + purchased.gems = gems + recipient.set('purchased', purchased) + recipient.save (err) -> + return res.send(500, '') if err + return res.send(201, '') + + handleSubscriptionDeleted = (req, res) -> + # Three variants: + # normal - Personal subscription deleted + # recipeint - Subscription sponsored by another user is being deleted. + # sponsor - Aggregate subscription used to pay for multiple recipient subscriptions. Ugh. + + subscription = req.body.data.object + + checkUserExists = (done) -> + stripe.customers.retrieve subscription.customer, (err, customer) => + if err + logStripeWebhookError("Failed to retrieve #{subscription.customer}") + return res.send(500, '') + unless customer?.metadata?.id + logStripeWebhookError("Customer with no metadata.id #{subscription.customer}") + return res.send(500, '') + User.findById customer.metadata.id, (err, user) => + if err + logStripeWebhookError(err) + return res.send(500, '') + unless user + logStripeWebhookError("User not found #{customer.metadata.id}") + return res.send(500, '') + return res.send(200, '') if user.get('deleted') is true + done() + + checkNormalSubscription = (done) -> + User.findOne {'stripe.subscriptionID': subscription.id}, (err, user) -> + return done() unless user + + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + delete stripeInfo.planID + delete stripeInfo.prepaidCode + delete stripeInfo.subscriptionID + user.set('stripe', stripeInfo) + user.save (err) => + if err + logStripeWebhookError(err) + return res.send(500, '') + return res.send(200, '') + + checkRecipientSubscription = (done) -> + return done() unless subscription.plan.id is 'basic' + return done() unless subscription.metadata?.id # Shouldn't be possible + + deleteUserStripeProp = (user, propName) -> + stripeInfo = _.cloneDeep(user.get('stripe') ? {}) + delete stripeInfo[propName] + if _.isEmpty stripeInfo + user.set 'stripe', undefined + else + user.set 'stripe', stripeInfo + + User.findById subscription.metadata.id, (err, recipient) => + if err + logStripeWebhookError(err) + return res.send(500, '') + unless recipient + logStripeWebhookError("Recipient not found #{subscription.metadata.id}") + return res.send(500, '') + + # Recipient cancellations are immediate, no work to perform if recipient's sponsorID is already gone + return res.send(200, '') unless recipient.get('stripe')?.sponsorID? + + User.findById recipient.get('stripe').sponsorID, (err, sponsor) => + if err + logStripeWebhookError(err) + return res.send(500, '') + unless sponsor + logStripeWebhookError("Sponsor not found #{recipient.get('stripe').sponsorID}") + return res.send(500, '') + + # Update sponsor subscription + stripeInfo = _.cloneDeep(sponsor.get('stripe') ? {}) + stripeInfo.recipients ?= [] + + if stripeInfo.sponsorSubscriptionID + _.remove(stripeInfo.recipients, (s) -> s.userID is recipient.id) + options = + quantity: utils.getSponsoredSubsAmount(subscription.plan.amount, stripeInfo.recipients.length, stripeInfo.subscriptionID?) + stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) => + if err + logStripeWebhookError(err) + return res.send(500, '') + + # Update sponsor user + sponsor.set 'stripe', stripeInfo + sponsor.save (err) => + if err + logStripeWebhookError(err) + return res.send(500, '') + + # Update recipient user + deleteUserStripeProp recipient, 'sponsorID' + recipient.save (err) => + if err + logStripeWebhookError(err) + return res.send(500, '') + return res.send(200, '') + else + # Remove sponsorships from sponsor and recipients + console.error "Couldn't find sponsorSubscriptionID from stripeInfo", stripeInfo, 'for customer', stripeInfo.customerID, 'with options', options, 'and subscription', subscription, 'for user', recipient.id, 'with sponsor', sponsor.id + + # Update recipients + createUpdateFn = (recipientID) -> + (callback) -> + User.findById recipientID, (err, recipient) => + if err + logStripeWebhookError(err) + return callback(err) + + deleteUserStripeProp recipient, 'sponsorID' + recipient.save (err) => + logStripeWebhookError(err) if err + callback(err) + async.parallel (createUpdateFn(recipient.userID) for recipient in stripeInfo.recipients), (err, results) => + if err + logStripeWebhookError(err) + return res.send(500, '') + + # Update sponsor + deleteUserStripeProp sponsor, 'recipients' + sponsor.save (err) => + if err + logStripeWebhookError(err) + return res.send(500, '') + return res.send(200, '') + + checkSponsorSubscription = (done) -> + return done() unless subscription.plan.id is 'incremental' + + customerID = subscription.customer + + createUpdateFn = (sub) -> + (callback) -> + # Cancel Stripe recipient subscription + stripe.customers.cancelSubscription customerID, sub.subscriptionID, { at_period_end: true }, (err) -> + callback err + + User.findById subscription.metadata.id, (err, sponsor) => + return res.send(500, '') if err + stripeInfo = _.cloneDeep(sponsor.get('stripe') ? {}) + + # Cancel all recipient subscriptions + async.parallel (createUpdateFn(sub) for sub in stripeInfo.recipients), (err, results) => + if err + logStripeWebhookError(err) + return res.send(500, '') + + # Update sponsor user + delete stripeInfo.sponsorSubscriptionID + delete stripeInfo.recipients # Loses remaining credit on a re-subscribe for previous user + if _.isEmpty stripeInfo + sponsor.set 'stripe', undefined + else + sponsor.set 'stripe', stripeInfo + sponsor.save (err) => + if err + logStripeWebhookError(err) + return res.send(500, '') + done() + + # TODO: use async.series for this + checkUserExists -> + checkNormalSubscription -> + checkRecipientSubscription -> + checkSponsorSubscription -> + res.send(200, '') diff --git a/server/sendwithus.coffee b/server/sendwithus.coffee index 656ec8401..328266b51 100644 --- a/server/sendwithus.coffee +++ b/server/sendwithus.coffee @@ -10,9 +10,15 @@ module.exports.api = new sendwithusAPI swuAPIKey, debug if config.unittest module.exports.api.send = -> module.exports.templates = + parent_subscribe_email: 'tem_2APERafogvwKhmcnouigud' + setup_free_sub_email: 'tem_sqdvLCZRwoDQc6jAf5RrQE' + share_progress_email: 'tem_VHE3ihhGmVa3727qds9zY8' welcome_email: 'utnGaBHuSU4Hmsi7qrAypU' ladder_update_email: 'JzaZxf39A4cKMxpPZUfWy4' patch_created: 'tem_xhxuNosLALsizTNojBjNcL' change_made_notify_watcher: 'tem_7KVkfmv9SZETb25dtHbUtG' recruiting_email: 'tem_mdFMgtcczHKYu94Jmq68j8' greed_tournament_rank: 'tem_c4KYnk2TriEkkZx5NqqGLG' + generic_email: 'tem_JhRnQ4pvTS4KdQjYoZdbei' + plain_text_email: 'tem_85UvKDCCNPXsFckERTig6Y' + next_steps_email: 'tem_RDHhTG5inXQi8pthyqWr5D' diff --git a/server/trial_requests/TrialRequest.coffee b/server/trial_requests/TrialRequest.coffee new file mode 100644 index 000000000..2abe7b096 --- /dev/null +++ b/server/trial_requests/TrialRequest.coffee @@ -0,0 +1,59 @@ +log = require 'winston' +mongoose = require 'mongoose' +config = require '../../server_config' +hipchat = require '../hipchat' +sendwithus = require '../sendwithus' +Prepaid = require '../prepaids/Prepaid' +jsonSchema = require '../../app/schemas/models/trial_request.schema' + +TrialRequestSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref} + +TrialRequestSchema.pre 'save', (next) -> + return next() unless @get('status') is 'approved' + Prepaid.generateNewCode (code) => + unless code + log.error "Trial request pre save prepaid gen new code failure" + return next() + prepaid = new Prepaid + creator: @get('reviewer') + type: 'subscription' + status: 'active' + code: code + properties: + couponID: 'free' + prepaid.save (err) => + if err + log.error "Trial request prepaid creation error: #{err}" + @set('prepaidCode', code) + next() + +TrialRequestSchema.post 'save', (doc) -> + if doc.get('status') is 'submitted' + msg = "<a href=\"http://codecombat.com/admin/trial-requests\">Trial Request</a> submitted by #{doc.get('properties').email}" + hipchat.sendHipChatMessage msg, ['tower'] + else if doc.get('status') is 'approved' + ppc = doc.get('prepaidCode') + unless ppc + log.error 'Trial request post save no ppc' + return + emailParams = + recipient: + address: doc.get('properties')?.email + email_id: sendwithus.templates.setup_free_sub_email + email_data: + url: "https://codecombat.com/account/subscription?_ppc=#{ppc}"; + sendwithus.api.send emailParams, (err, result) => + log.error "sendwithus trial request approved error: #{err}, result: #{result}" if err + +TrialRequestSchema.statics.privateProperties = [] +TrialRequestSchema.statics.editableProperties = [ + 'prepaidCode' + 'properties' + 'reviewDate' + 'reviewer' + 'status' + 'type' +] + +TrialRequestSchema.statics.jsonSchema = jsonSchema +module.exports = TrialRequest = mongoose.model 'trial.request', TrialRequestSchema, 'trial.requests' diff --git a/server/trial_requests/trial_request_handler.coffee b/server/trial_requests/trial_request_handler.coffee new file mode 100644 index 000000000..bbffa9b3e --- /dev/null +++ b/server/trial_requests/trial_request_handler.coffee @@ -0,0 +1,40 @@ +async = require 'async' +log = require 'winston' +mongoose = require 'mongoose' +Handler = require '../commons/Handler' +TrialRequest = require './TrialRequest' + +TrialRequestHandler = class TrialRequestHandler extends Handler + modelClass: TrialRequest + jsonSchema: require '../../app/schemas/models/trial_request.schema' + + hasAccess: (req) -> + req.method in ['POST'] or req.user?.isAdmin() + + hasAccessToDocument: (req, document, method=null) -> + return false unless document? + return true if req.user?.isAdmin() + false + + makeNewInstance: (req) -> + instance = super(req) + instance.set 'applicant', req.user._id + instance.set 'status', 'submitted' + instance + + put: (req, res, id) -> + req.body.reviewDate = new Date() + req.body.reviewer = req.user.get('_id') + super(req, res, id) + + getByRelationship: (req, res, args...) -> + return @getOwn(req, res) if args[1] is 'own' + super(arguments...) + + getOwn: (req, res) -> + return @sendForbiddenError(res) unless req.user? + TrialRequest.find {applicant: req.user.get('_id')}, (err, documents) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, documents) + +module.exports = new TrialRequestHandler() diff --git a/server/user_code_problems/UserCodeProblem.coffee b/server/user_code_problems/UserCodeProblem.coffee index f03168b34..e901a7705 100644 --- a/server/user_code_problems/UserCodeProblem.coffee +++ b/server/user_code_problems/UserCodeProblem.coffee @@ -1,10 +1,11 @@ mongoose = require 'mongoose' plugins = require '../plugins/plugins' +config = require '../../server_config' UserCodeProblemSchema = new mongoose.Schema({ created: type: Date 'default': Date.now -}, {strict: false}) +}, {strict: false,read:config.mongo.readpref}) module.exports = UserCodeProblem = mongoose.model('user.code.problem', UserCodeProblemSchema) diff --git a/server/user_code_problems/user_code_problem_handler.coffee b/server/user_code_problems/user_code_problem_handler.coffee index a4424b6f9..968e2d2c2 100644 --- a/server/user_code_problems/user_code_problem_handler.coffee +++ b/server/user_code_problems/user_code_problem_handler.coffee @@ -1,5 +1,6 @@ UserCodeProblem = require './UserCodeProblem' Handler = require '../commons/Handler' +utils = require '../lib/utils' class UserCodeProblemHandler extends Handler modelClass: UserCodeProblem @@ -23,4 +24,68 @@ class UserCodeProblemHandler extends Handler ucp.set('creator', req.user._id) ucp + hasAccess: (req) -> + return true if req.user?.isAdmin() + return true if req.method.toLowerCase() is 'post' + false + + getByRelationship: (req, res, args...) -> + return @sendForbiddenError res unless @hasAccess req + return @getCommonLevelProblemsBySlug(req, res) if args[1] is 'common_problems' + super(arguments...) + + getCommonLevelProblemsBySlug: (req, res) -> + # Returns an ordered array of common user code problems with: language, message, hint, count + # Parameters: + # slug - level slug + # startDay - Inclusive, optional, e.g. '2014-12-14' + # endDay - Exclusive, optional, e.g. '2014-12-16' + + levelSlug = req.query.slug or req.body.slug + startDay = req.query.startDay or req.body.startDay + endDay = req.query.endDay or req.body.endDay + + return @sendSuccess res, [] unless levelSlug? + + # Cache results for 1 day + @commonLevelProblemsCache ?= {} + @commonLevelProblemsCachedSince ?= new Date() + if (new Date()) - @commonLevelProblemsCachedSince > 86400 * 1000 # Dumb cache expiration + @commonLevelProblemsCache = {} + @commonLevelProblemsCachedSince = new Date() + cacheKey = levelSlug + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + return @sendSuccess res, commonProblems if commonProblems = @commonLevelProblemsCache[cacheKey] + + # Build query + match = if startDay? or endDay? then {$match: {$and: []}} else {$match: {}} + match["$match"]["$and"].push _id: {$gte: utils.objectIdFromTimestamp(startDay + "T00:00:00.000Z")} if startDay? + match["$match"]["$and"].push _id: {$lt: utils.objectIdFromTimestamp(endDay + "T00:00:00.000Z")} if endDay? + group = {"$group": {"_id": {"errMessage": "$errMessageNoLineInfo", "errHint": "$errHint", "language": "$language", "levelID": "$levelID"}, "count": {"$sum": 1}}} + sort = { $sort : { "_id.levelID": 1, count : -1, "_id.language": 1 } } + query = UserCodeProblem.aggregate match, group, sort + + query.exec (err, data) => + if err? then return @sendDatabaseError res, err + + # Build per-level common problem lists + commonProblems = {} + for item in data + levelID = item._id.levelID + commonProblems[levelID] ?= [] + commonProblems[levelID].push + language: item._id.language + message: item._id.errMessage + hint: item._id.errHint + count: item.count + + # Cache all the levels + for levelID of commonProblems + cacheKey = levelID + cacheKey += 's' + startDay if startDay? + cacheKey += 'e' + endDay if endDay? + @commonLevelProblemsCache[cacheKey] = commonProblems[levelID] + @sendSuccess res, commonProblems[levelSlug] + module.exports = new UserCodeProblemHandler() diff --git a/server/users/User.coffee b/server/users/User.coffee index 505d2375a..f5da99711 100644 --- a/server/users/User.coffee +++ b/server/users/User.coffee @@ -5,6 +5,10 @@ crypto = require 'crypto' mail = require '../commons/mail' log = require 'winston' plugins = require '../plugins/plugins' +AnalyticsUsersActive = require '../analytics/AnalyticsUsersActive' + +config = require '../../server_config' +stripe = require('stripe')(config.stripe.secretKey) sendwithus = require '../sendwithus' delighted = require '../delighted' @@ -15,17 +19,43 @@ UserSchema = new mongoose.Schema({ 'default': Date.now }, {strict: false}) +UserSchema.index({'dateCreated': 1}) +UserSchema.index({'emailLower': 1}, {unique: true, sparse: true, name: 'emailLower_1'}) +UserSchema.index({'facebookID': 1}, {sparse: true}) +UserSchema.index({'gplusID': 1}, {sparse: true}) +UserSchema.index({'iosIdentifierForVendor': 1}, {name: 'iOS identifier for vendor', sparse: true, unique: true}) +UserSchema.index({'mailChimp.leid': 1}, {sparse: true}) +UserSchema.index({'nameLower': 1}, {sparse: true, name: 'nameLower_1'}) +UserSchema.index({'simulatedBy': 1}) +UserSchema.index({'slug': 1}, {name: 'slug index', sparse: true, unique: true}) +UserSchema.index({'stripe.subscriptionID': 1}, {unique: true, sparse: true}) +UserSchema.index({'siteref': 1}, {name: 'siteref index', sparse: true}) + UserSchema.post('init', -> @set('anonymous', false) if @get('email') ) +UserSchema.methods.isInGodMode = -> + p = @get('permissions') + return p and 'godmode' in p + UserSchema.methods.isAdmin = -> p = @get('permissions') return p and 'admin' in p +UserSchema.methods.isArtisan = -> + p = @get('permissions') + return p and 'artisan' in p + UserSchema.methods.isAnonymous = -> @get 'anonymous' +UserSchema.methods.getUserInfo = -> + info = + id : @get('_id') + email : if @get('anonymous') then 'Unregistered User' else @get('email') + return info + UserSchema.methods.trackActivity = (activityName, increment) -> now = new Date() increment ?= parseInt increment or 1 @@ -59,9 +89,10 @@ UserSchema.methods.setEmailSubscription = (newName, enabled) -> newSubs[newName].enabled = enabled @set('emails', newSubs) @newsSubsChanged = true if newName in mail.NEWS_GROUPS - + UserSchema.methods.gems = -> gemsEarned = @get('earned')?.gems ? 0 + gemsEarned = gemsEarned + 100000 if @isInGodMode() gemsPurchased = @get('purchased')?.gems ? 0 gemsSpent = @get('spent') ? 0 gemsEarned + gemsPurchased - gemsSpent @@ -76,12 +107,19 @@ UserSchema.methods.isEmailSubscriptionEnabled = (newName) -> _.defaults emails, _.cloneDeep(jsonschema.properties.emails.default) return emails[newName]?.enabled -UserSchema.statics.updateMailChimp = (doc, callback) -> +UserSchema.statics.updateServiceSettings = (doc, callback) -> return callback?() unless isProduction or GLOBAL.testing return callback?() if doc.updatedMailChimp return callback?() unless doc.get('email') existingProps = doc.get('mailChimp') emailChanged = (not existingProps) or existingProps?.email isnt doc.get('email') + + if emailChanged and customerID = doc.get('stripe')?.customerID + unless stripe?.customers + console.error('Oh my god, Stripe is not imported correctly-how could we have done this (again)?') + stripe?.customers?.update customerID, {email:doc.get('email')}, (err, customer) -> + console.error('Error updating stripe customer...', err) if err + return callback?() unless emailChanged or doc.newsSubsChanged newGroups = [] @@ -94,11 +132,14 @@ UserSchema.statics.updateMailChimp = (doc, callback) -> params = {} params.id = mail.MAILCHIMP_LIST_ID params.email = if existingProps then {leid: existingProps.leid} else {email: doc.get('email')} - params.merge_vars = {groupings: [{id: mail.MAILCHIMP_GROUP_ID, groups: newGroups}]} + params.merge_vars = { + groupings: [{id: mail.MAILCHIMP_GROUP_ID, groups: newGroups}] + 'new-email': doc.get('email') + } params.update_existing = true - params.double_optin = true onSuccess = (data) -> + data.email = doc.get('email') # Make sure that we don't spam opt-in emails even if MailChimp doesn't update the email it gets in this object until they have confirmed. doc.set('mailChimp', data) doc.updatedMailChimp = true doc.save() @@ -109,7 +150,7 @@ UserSchema.statics.updateMailChimp = (doc, callback) -> doc.updatedMailChimp = true callback?() - mc?.lists.subscribe params, onSuccess, onFailure + mc?.lists.subscribe params, onSuccess, onFailure unless config.unittest UserSchema.statics.statsMapping = edits: @@ -118,18 +159,27 @@ UserSchema.statics.statsMapping = 'level.component': 'stats.levelComponentEdits' 'level.system': 'stats.levelSystemEdits' 'thang.type': 'stats.thangTypeEdits' + 'Achievement': 'stats.achievementEdits' + 'campaign': 'stats.campaignEdits' + 'poll': 'stats.pollEdits' translations: article: 'stats.articleTranslationPatches' level: 'stats.levelTranslationPatches' 'level.component': 'stats.levelComponentTranslationPatches' 'level.system': 'stats.levelSystemTranslationPatches' 'thang.type': 'stats.thangTypeTranslationPatches' + 'Achievement': 'stats.achievementTranslationPatches' + 'campaign': 'stats.campaignTranslationPatches' + 'poll': 'stats.pollTranslationPatches' misc: article: 'stats.articleMiscPatches' level: 'stats.levelMiscPatches' 'level.component': 'stats.levelComponentMiscPatches' 'level.system': 'stats.levelSystemMiscPatches' 'thang.type': 'stats.thangTypeMiscPatches' + 'Achievement': 'stats.achievementMiscPatches' + 'campaign': 'stats.campaignMiscPatches' + 'poll': 'stats.pollMiscPatches' UserSchema.statics.incrementStat = (id, statName, done, inc=1) -> id = mongoose.Types.ObjectId id if _.isString id @@ -137,7 +187,7 @@ UserSchema.statics.incrementStat = (id, statName, done, inc=1) -> log.error err if err? err = new Error "Could't find user with id '#{id}'" unless user or err return done() if err? - user.incrementStat statName, done, inc=1 + user.incrementStat statName, done, inc UserSchema.methods.incrementStat = (statName, done, inc=1) -> @set statName, (@get(statName) or 0) + inc @@ -152,24 +202,79 @@ UserSchema.statics.unconflictName = unconflictName = (name, done) -> UserSchema.methods.register = (done) -> @set('anonymous', false) - @set('permissions', ['admin']) if not isProduction + @set('permissions', ['admin']) if not isProduction and not GLOBAL.testing if (name = @get 'name')? and name isnt '' unconflictName name, (err, uniqueName) => return done err if err @set 'name', uniqueName done() else done() - data = - email_id: sendwithus.templates.welcome_email - recipient: - address: @get 'email' - sendwithus.api.send data, (err, result) -> - log.error "sendwithus post-save error: #{err}, result: #{result}" if err - delighted.addDelightedUser @ + if @isEmailSubscriptionEnabled 'generalNews' + data = + email_id: sendwithus.templates.welcome_email + recipient: + address: @get 'email' + sendwithus.api.send data, (err, result) -> + log.error "sendwithus post-save error: #{err}, result: #{result}" if err + delighted.addDelightedUser @ + @saveActiveUser 'register' + +UserSchema.methods.isPremium = -> + return true if @isInGodMode() + return true if @isAdmin() + return false unless stripeObject = @get('stripe') + return true if stripeObject.sponsorID + return true if stripeObject.subscriptionID + return true if stripeObject.free is true + return true if _.isString(stripeObject.free) and new Date() < new Date(stripeObject.free) + return false + +UserSchema.methods.level = -> + xp = @get('points') or 0 + a = 5 + b = c = 100 + if xp > 0 then Math.floor(a * Math.log((1/b) * (xp + c))) + 1 else 1 + +UserSchema.statics.saveActiveUser = (id, event, done=null) -> + # TODO: Disabling this until we know why our app servers CPU grows out of control. + return done?() + id = mongoose.Types.ObjectId id if _.isString id + @findById id, (err, user) -> + if err? + log.error err + else + user?.saveActiveUser event + done?() + +UserSchema.methods.saveActiveUser = (event, done=null) -> + # TODO: Disabling this until we know why our app servers CPU grows out of control. + return done?() + try + return done?() if @isAdmin() + userID = @get('_id') + + # Create if no active user entry for today + today = new Date() + minDate = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate())) + AnalyticsUsersActive.findOne({created: {$gte: minDate}, creator: mongoose.Types.ObjectId(userID)}).exec (err, activeUser) -> + if err? + log.error "saveActiveUser error retrieving active users: #{err}" + else if not activeUser + newActiveUser = new AnalyticsUsersActive() + newActiveUser.set 'creator', userID + newActiveUser.set 'event', event + newActiveUser.save (err) -> + log.error "Level session saveActiveUser error saving active user: #{err}" if err? + done?() + catch err + log.error err + done?() UserSchema.pre('save', (next) -> - @set('emailLower', @get('email')?.toLowerCase()) - @set('nameLower', @get('name')?.toLowerCase()) + if email = @get('email') + @set('emailLower', email.toLowerCase()) + if name = @get('name') + @set('nameLower', name.toLowerCase()) pwd = @get('password') if @get('password') @set('passwordHash', User.hashPassword(pwd)) @@ -181,7 +286,11 @@ UserSchema.pre('save', (next) -> ) UserSchema.post 'save', (doc) -> - UserSchema.statics.updateMailChimp(doc) + doc.newsSubsChanged = not _.isEqual(_.pick(doc.get('emails'), mail.NEWS_GROUPS), _.pick(doc.startingEmails, mail.NEWS_GROUPS)) + UserSchema.statics.updateServiceSettings(doc) + +UserSchema.post 'init', (doc) -> + doc.startingEmails = _.cloneDeep(doc.get('emails')) UserSchema.statics.hashPassword = (password) -> password = password.toLowerCase() @@ -192,15 +301,15 @@ UserSchema.statics.hashPassword = (password) -> UserSchema.statics.privateProperties = [ 'permissions', 'email', 'mailChimp', 'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'music', 'volume', 'aceConfig', 'employerAt', 'signedEmployerAgreement', - 'emailSubscriptions', 'emails', 'activity', 'stripeCustomerID' + 'emailSubscriptions', 'emails', 'activity', 'stripe', 'stripeCustomerID', 'chinaVersion' ] UserSchema.statics.jsonSchema = jsonschema UserSchema.statics.editableProperties = [ 'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume', - 'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emails', + 'firstName', 'lastName', 'gender', 'ageRange', 'facebookID', 'gplusID', 'emails', 'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage', 'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile', 'savedEmployerFilterAlerts', - 'heroConfig', 'iosIdentifierForVendor' + 'heroConfig', 'iosIdentifierForVendor', 'siteref', 'referrer' ] UserSchema.plugin plugins.NamedPlugin diff --git a/server/users/remarks/UserRemark.coffee b/server/users/remarks/UserRemark.coffee index 445340c0b..15eb63a2b 100644 --- a/server/users/remarks/UserRemark.coffee +++ b/server/users/remarks/UserRemark.coffee @@ -8,4 +8,6 @@ UserRemarkSchema = new mongoose.Schema({ 'default': Date.now }, {strict: false}) +UserRemarkSchema.index({user: 1}, {name: 'User'}) + module.exports = UserRemark = mongoose.model('user.remark', UserRemarkSchema) diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index effa097de..ae1dea8a0 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -9,11 +9,19 @@ errors = require '../commons/errors' async = require 'async' log = require 'winston' moment = require 'moment' +AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent' +Clan = require '../clans/Clan' LevelSession = require '../levels/sessions/LevelSession' LevelSessionHandler = require '../levels/sessions/level_session_handler' +Payment = require '../payments/Payment' +SubscriptionHandler = require '../payments/subscription_handler' +DiscountHandler = require '../payments/discount_handler' EarnedAchievement = require '../achievements/EarnedAchievement' UserRemark = require './remarks/UserRemark' +{findStripeSubscription} = require '../lib/utils' {isID} = require '../lib/utils' +hipchat = require '../hipchat' +sendwithus = require '../sendwithus' serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset', 'lastIP'] candidateProperties = [ @@ -23,6 +31,8 @@ candidateProperties = [ UserHandler = class UserHandler extends Handler modelClass: User + allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] + getEditableProperties: (req, document) -> props = super req, document props.push 'permissions' unless config.isProduction @@ -105,6 +115,58 @@ UserHandler = class UserHandler extends Handler return callback({res: r, code: 409}) if otherUser user.set('name', req.body.name) callback(null, req, user) + + # Subscription setting + (req, user, callback) -> + # TODO: Make subscribe vs. unsubscribe explicit. This property dance is confusing. + return callback(null, req, user) unless req.headers['x-change-plan'] # ensure only saves that are targeted at changing the subscription actually affect the subscription + return callback(null, req, user) unless req.body.stripe + finishSubscription = (hasPlan, wantsPlan) -> + return callback(null, req, user) if hasPlan is wantsPlan + if wantsPlan and not hasPlan + SubscriptionHandler.subscribeUser(req, user, (err) -> + return callback(err) if err + return callback(null, req, user) + ) + else if hasPlan and not wantsPlan + SubscriptionHandler.unsubscribeUser(req, user, (err) -> + return callback(err) if err + return callback(null, req, user) + ) + if req.body.stripe.subscribeEmails? + SubscriptionHandler.subscribeUser(req, user, (err) -> + return callback(err) if err + return callback(null, req, user) + ) + else if req.body.stripe.unsubscribeEmail? + SubscriptionHandler.unsubscribeUser(req, user, (err) -> + return callback(err) if err + return callback(null, req, user) + ) + else + wantsPlan = req.body.stripe.planID? + hasPlan = user.get('stripe')?.planID? and not req.body.stripe.prepaidCode? + finishSubscription hasPlan, wantsPlan + + # Discount setting + (req, user, callback) -> + return callback(null, req, user) unless req.body.stripe + return callback(null, req, user) unless req.user?.isAdmin() + hasCoupon = user.get('stripe')?.couponID + wantsCoupon = req.body.stripe.couponID + + return callback(null, req, user) if hasCoupon is wantsCoupon + if wantsCoupon and (hasCoupon isnt wantsCoupon) + DiscountHandler.discountUser(req, user, (err) -> + return callback(err) if err + return callback(null, req, user) + ) + else if hasCoupon and not wantsCoupon + DiscountHandler.removeDiscountFromCustomer(req, user, (err) -> + return callback(err) if err + return callback(null, req, user) + ) + ] getById: (req, res, id) -> @@ -112,10 +174,17 @@ UserHandler = class UserHandler extends Handler return @sendSuccess(res, @formatEntity(req, req.user, 256)) super(req, res, id) + getByIDs: (req, res) -> + return @sendForbiddenError(res) unless req.user?.isAdmin() + User.find {_id: {$in: req.body.ids}}, (err, users) => + return @sendDatabaseError(res, err) if err + cleandocs = (@formatEntity(req, doc) for doc in users) + @sendSuccess(res, cleandocs) + getNamesByIDs: (req, res) -> ids = req.query.ids or req.body.ids returnWizard = req.query.wizard or req.body.wizard - properties = if returnWizard then 'name wizard' else 'name' + properties = if returnWizard then 'name wizard firstName lastName' else 'name firstName lastName' @getPropertiesFromMultipleDocuments res, User, properties, ids nameToID: (req, res, name) -> @@ -126,11 +195,12 @@ UserHandler = class UserHandler extends Handler getSimulatorLeaderboard: (req, res) -> queryParameters = @getSimulatorLeaderboardQueryParameters(req) leaderboardQuery = User.find(queryParameters.query).select('name simulatedBy simulatedFor').sort({'simulatedBy': queryParameters.sortOrder}).limit(queryParameters.limit) + leaderboardQuery.cache() if req.query.scoreOffset is -1 leaderboardQuery.exec (err, otherUsers) -> - otherUsers = _.reject otherUsers, _id: req.user._id if req.query.scoreOffset isnt -1 - otherUsers ?= [] - res.send(otherUsers) - res.end() + otherUsers = _.reject otherUsers, _id: req.user._id if req.query.scoreOffset isnt -1 and req.user + otherUsers ?= [] + res.send(otherUsers) + res.end() getMySimulatorLeaderboardRank: (req, res) -> req.query.order = 1 @@ -167,20 +237,78 @@ UserHandler = class UserHandler extends Handler @put(req, res) hasAccessToDocument: (req, document) -> - if req.route.method in ['put', 'post', 'patch'] + if req.route.method in ['put', 'post', 'patch', 'delete'] return true if req.user?.isAdmin() return req.user?._id.equals(document._id) return true + delete: (req, res, userID) -> + # Instead of just deleting the User object, we should remove all the properties except for _id + # And add a `deleted: true` property + @getDocumentForIdOrSlug userID, (err, user) => # Check first + return @sendDatabaseError res, err if err + return @sendNotFoundError res unless user + return @sendForbiddenError res unless @hasAccessToDocument(req, user) + + # Delete subscriptions attached to this user first + # TODO: check sponsored subscriptions (stripe.recipients) + checkPersonalSubscription = (user, done) => + try + return done() unless user.get('stripe')?.subscriptionID? + SubscriptionHandler.unsubscribeUser {body: user.toObject()}, user, (err) => + log.error("User delete check personal sub " + err) if err + done() + catch error + log.error("User delete check personal sub " + error) + done() + checkRecipientSubscription = (user, done) => + try + return done() unless sponsorID = user.get('stripe')?.sponsorID + User.findById sponsorID, (err, sponsor) => + if err + log.error("User delete check recipient sub " + err) + return done() + unless sponsor + log.error("User delete check recipient sub no sponsor #{user.get('stripe').sponsorID}") + return done() + sponsorObject = sponsor.toObject() + sponsorObject.stripe.unsubscribeEmail = user.get('email') + SubscriptionHandler.unsubscribeRecipient {body: sponsorObject}, sponsor, (err) => + log.error("User delete check recipient sub " + err) if err + done() + catch error + log.error("User delete check recipient sub " + error) + done() + deleteSubscriptions = (user, done) => + checkPersonalSubscription user, (err) => + checkRecipientSubscription user, done + + deleteSubscriptions user, => + obj = user.toObject() + for prop, val of obj + user.set(prop, undefined) unless prop is '_id' + user.set('dateDeleted', new Date()) + user.set('deleted', true) + + # Hack to get saving of Users to work. Probably should replace these props with strings + # so that validation doesn't get hung up on Date objects in the documents. + delete obj.dateCreated + + user.save (err) => + return @sendDatabaseError(res, err) if err + @sendNoContent res + getByRelationship: (req, res, args...) -> return @agreeToCLA(req, res) if args[1] is 'agreeToCLA' return @agreeToEmployerAgreement(req, res) if args[1] is 'agreeToEmployerAgreement' return @avatar(req, res, args[0]) if args[1] is 'avatar' + return @getByIDs(req, res) if args[1] is 'users' return @getNamesByIDs(req, res) if args[1] is 'names' return @nameToID(req, res, args[0]) if args[1] is 'nameToID' return @getLevelSessionsForEmployer(req, res, args[0]) if args[1] is 'level.sessions' and args[2] is 'employer' return @getLevelSessions(req, res, args[0]) if args[1] is 'level.sessions' return @getCandidates(req, res) if args[1] is 'candidates' + return @getClans(req, res, args[0]) if args[1] is 'clans' return @getEmployers(req, res) if args[1] is 'employers' return @getSimulatorLeaderboard(req, res, args[0]) if args[1] is 'simulatorLeaderboard' return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank' @@ -189,9 +317,140 @@ UserHandler = class UserHandler extends Handler return @trackActivity(req, res, args[0], args[2], args[3]) if args[1] is 'track' and args[2] return @getRemark(req, res, args[0]) if args[1] is 'remark' return @searchForUser(req, res) if args[1] is 'admin_search' + return @getStripeInfo(req, res, args[0]) if args[1] is 'stripe' + return @getSubRecipients(req, res) if args[1] is 'sub_recipients' + return @getSubSponsor(req, res) if args[1] is 'sub_sponsor' + return @getSubSponsors(req, res) if args[1] is 'sub_sponsors' + return @sendOneTimeEmail(req, res, args[0]) if args[1] is 'send_one_time_email' return @sendNotFoundError(res) super(arguments...) + getStripeInfo: (req, res, handle) -> + @getDocumentForIdOrSlug handle, (err, user) => + return @sendNotFoundError(res) if not user + return @sendForbiddenError(res) unless req.user and (req.user.isAdmin() or req.user.get('_id').equals(user.get('_id'))) + return @sendNotFoundError(res) if not customerID = user.get('stripe')?.customerID + stripe.customers.retrieve customerID, (err, customer) => + return @sendDatabaseError(res, err) if err + info = card: customer.sources?.data?[0] + findStripeSubscription customerID, subscriptionID: user.get('stripe').subscriptionID, (subscription) => + info.subscription = subscription + findStripeSubscription customerID, subscriptionID: user.get('stripe').sponsorSubscriptionID, (subscription) => + info.sponsorSubscription = subscription + @sendSuccess(res, JSON.stringify(info, null, '\t')) + + getSubRecipients: (req, res) -> + # Return map of userIDs to name/email/cancel date + # TODO: Add test for this API + + return @sendSuccess(res, {}) if _.isEmpty(req.user?.get('stripe')?.recipients ? []) + return @sendSuccess(res, {}) unless req.user.get('stripe')?.customerID? + + # Get recipients User info + ids = (recipient.userID for recipient in req.user.get('stripe').recipients) + User.find({'_id': { $in: ids} }, 'name emailLower').exec (err, users) => + info = {} + _.each users, (user) -> info[user.id] = user.toObject() + customerID = req.user.get('stripe').customerID + + nextBatch = (starting_after, done) -> + options = limit: 100 + options.starting_after = starting_after if starting_after + stripe.customers.listSubscriptions customerID, options, (err, subscriptions) -> + return done(err) if err + return done() unless subscriptions?.data?.length > 0 + for sub in subscriptions.data + userID = sub.metadata?.id + continue unless userID of info + if sub.cancel_at_period_end and info[userID]['cancel_at_period_end'] isnt false + info[userID]['cancel_at_period_end'] = new Date(sub.current_period_end * 1000) + else + info[userID]['cancel_at_period_end'] = false + + if subscriptions.has_more + return nextBatch(subscriptions.data[subscriptions.data.length - 1].id, done) + else + return done() + nextBatch null, (err) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, info) + + getSubSponsor: (req, res) -> + # TODO: Add test for this API + + return @sendSuccess(res, {}) unless req.user?.get('stripe')?.sponsorID? + + # Get sponsor User info + User.findById req.user.get('stripe').sponsorID, (err, sponsor) => + return @sendDatabaseError(res, err) if err + return @sendDatabaseError(res, 'No sponsor customerID') unless sponsor?.get('stripe')?.customerID? + info = + email: sponsor.get('emailLower') + name: sponsor.get('name') + + # Get recipient subscription info + findStripeSubscription sponsor.get('stripe').customerID, userID: req.user.id, (subscription) => + info.subscription = subscription + @sendDatabaseError(res, 'No sponsored subscription found') unless info.subscription? + @sendSuccess(res, info) + + getSubSponsors: (req, res) -> + return @sendForbiddenError(res) unless req.user?.isAdmin() + Payment.find {$where: 'this.purchaser.valueOf() != this.recipient.valueOf()'}, (err, payments) => + return @sendDatabaseError(res, err) if err + sponsorIDs = (payment.get('purchaser') for payment in payments) + User.find {$and: [{_id: {$in: sponsorIDs}}, {"stripe.sponsorSubscriptionID": {$exists: true}}]}, (err, users) => + return @sendDatabaseError(res, err) if err + sponsors = (@formatEntity(req, doc) for doc in users when doc.get('stripe').recipients?.length > 0) + @sendSuccess(res, sponsors) + + sendOneTimeEmail: (req, res) -> + # TODO: Should this API be somewhere else? + # TODO: Where should email types be stored? + # TODO: How do we schema validate an update db call? + + return @sendForbiddenError(res) unless req.user + email = req.query.email or req.body.email + type = req.query.type or req.body.type + return @sendBadInputError res, 'No email given.' unless email? + return @sendBadInputError res, 'No type given.' unless type? + + # log.warn "sendOneTimeEmail #{type} #{email}" + + unless type in ['subscribe modal parent', 'share progress modal parent'] + return @sendBadInputError res, "Unknown one-time email type #{type}" + + sendMail = (emailParams) => + sendwithus.api.send emailParams, (err, result) => + if err + log.error "sendwithus one-time email error: #{err}, result: #{result}" + return @sendError res, 500, 'send mail failed.' + req.user.update {$push: {"emails.oneTimes": {type: type, email: email, sent: new Date()}}}, (err) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, {result: 'success'}) + AnalyticsLogEvent.logEvent req.user, 'Sent one time email', email: email, type: type + + # Generic email data + emailParams = + recipient: + address: email + email_data: + name: req.user.get('name') or '' + if codeLanguage = req.user.get('aceConfig.language') + codeLanguage = codeLanguage[0].toUpperCase() + codeLanguage.slice(1) + codeLanguage = codeLanguage.replace 'script', 'Script' + emailParams['email_data']['codeLanguage'] = codeLanguage + if senderEmail = req.user.get('email') + emailParams['email_data']['senderEmail'] = senderEmail + + # Type-specific email data + if type is 'subscribe modal parent' + emailParams['email_id'] = sendwithus.templates.parent_subscribe_email + else if type is 'share progress modal parent' + emailParams['email_id'] = sendwithus.templates.share_progress_email + + sendMail emailParams + agreeToCLA: (req, res) -> return @sendForbiddenError(res) unless req.user doc = @@ -208,6 +467,7 @@ UserHandler = class UserHandler extends Handler req.user.save (err) => return @sendDatabaseError(res, err) if err @sendSuccess(res, {result: 'success'}) + hipchat.sendHipChatMessage "#{req.body.githubUsername or req.user.get('name')} just signed the CLA.", ['main'] avatar: (req, res, id) -> @modelClass.findById(id).exec (err, document) => @@ -265,9 +525,6 @@ UserHandler = class UserHandler extends Handler EarnedAchievement.find(query).sort(changed: -1).exec (err, documents) => return @sendDatabaseError(res, err) if err? cleandocs = (@formatEntity(req, doc) for doc in documents) - for doc in documents # TODO Ruben Maybe move this logic elsewhere - doc.set('notified', true) - doc.save() @sendSuccess(res, cleandocs) getRecentlyPlayed: (req, res, userID) -> @@ -324,6 +581,7 @@ UserHandler = class UserHandler extends Handler getCandidates: (req, res) -> return @sendForbiddenError(res) unless req.user + return @sendForbiddenError(res) # No one can view the candidates, since in a rush, we deleted their index! authorized = req.user.isAdmin() or ('employer' in (req.user.get('permissions') ? [])) months = if req.user.isAdmin() then 12 else 2 since = (new Date((new Date()) - months * 30.4 * 86400 * 1000)).toISOString() @@ -337,6 +595,16 @@ UserHandler = class UserHandler extends Handler candidates = (@formatCandidate(authorized, candidate) for candidate in candidates) @sendSuccess(res, candidates) + getClans: (req, res, userIDOrSlug) -> + @getDocumentForIdOrSlug userIDOrSlug, (err, user) => + return @sendNotFoundError(res) unless user + clanIDs = user.get('clans') ? [] + query = {$and: [{_id: {$in: clanIDs}}]} + query['$and'].push {type: 'public'} unless req.user?.id is user.id + Clan.find query, (err, documents) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, documents) + formatCandidate: (authorized, document) -> fields = if authorized then ['name', 'jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['_id','jobProfile', 'jobProfileApproved'] obj = _.pick document.toObject(), fields @@ -359,6 +627,7 @@ UserHandler = class UserHandler extends Handler getEmployers: (req, res) -> return @sendForbiddenError(res) unless req.user?.isAdmin() + return @sendForbiddenError(res) # No one can view the employers, since in a rush, we deleted their index! query = {employerAt: {$exists: true, $ne: ''}} selection = 'name firstName lastName email activity signedEmployerAgreement photoURL employerAt' User.find(query).select(selection).lean().exec (err, documents) => @@ -414,18 +683,23 @@ UserHandler = class UserHandler extends Handler countEdits = (model, done) -> statKey = User.statsMapping.edits[model.modelName] return done(new Error 'Could not resolve statKey for model') unless statKey? - userStream = User.find().stream() + userStream = User.find({anonymous: false}).sort('_id').stream() streamFinished = false usersTotal = 0 usersFinished = 0 + numberRunning = 0 doneWithUser = (err) -> log.error err if err? ++usersFinished + --numberRunning + userStream.resume() done?() if streamFinished and usersFinished is usersTotal userStream.on 'error', (err) -> log.error err userStream.on 'close', -> streamFinished = true userStream.on 'data', (user) -> - usersTotal += 1 + ++usersTotal + ++numberRunning + userStream.pause() if numberRunning > 20 userObjectID = user.get('_id') userStringID = userObjectID.toHexString() @@ -436,6 +710,7 @@ UserHandler = class UserHandler extends Handler else update = $unset: {} update.$unset[statKey] = '' + console.log "... updating #{userStringID} patches #{statKey} to #{count}, #{usersTotal} players found so far." if count User.findByIdAndUpdate user.get('_id'), update, (err) -> log.error err if err? doneWithUser() @@ -461,20 +736,26 @@ UserHandler = class UserHandler extends Handler update = {} update[method] = {} update[method][statName] = count or '' + console.log "... updating #{user.get('_id')} patches #{JSON.stringify(query)} #{statName} to #{count}, #{usersTotal} players found so far." if count User.findByIdAndUpdate user.get('_id'), update, doneUpdatingUser - userStream = User.find().stream() + userStream = User.find({anonymous: false}).sort('_id').stream() streamFinished = false usersTotal = 0 usersFinished = 0 + numberRunning = 0 doneWithUser = (err) -> log.error err if err? ++usersFinished + --numberRunning + userStream.resume() done?() if streamFinished and usersFinished is usersTotal userStream.on 'error', (err) -> log.error err userStream.on 'close', -> streamFinished = true userStream.on 'data', (user) -> - usersTotal += 1 + ++usersTotal + ++numberRunning + userStream.pause() if numberRunning > 20 userObjectID = user.get '_id' userStringID = userObjectID.toHexString() # Extend query with a patch ownership test @@ -492,18 +773,23 @@ UserHandler = class UserHandler extends Handler countPatchesByUsers = (query, statName, done) -> Patch = require '../patches/Patch' - userStream = User.find().stream() + userStream = User.find({anonymous: false}).sort('_id').stream() streamFinished = false usersTotal = 0 usersFinished = 0 + numberRunning = 0 doneWithUser = (err) -> log.error err if err? ++usersFinished + --numberRunning + userStream.resume() done?() if streamFinished and usersFinished is usersTotal userStream.on 'error', (err) -> log.error err userStream.on 'close', -> streamFinished = true userStream.on 'data', (user) -> - usersTotal += 1 + ++usersTotal + ++numberRunning + userStream.pause() if numberRunning > 20 userObjectID = user.get '_id' userStringID = userObjectID.toHexString() # Extend query with a patch ownership test @@ -514,28 +800,37 @@ UserHandler = class UserHandler extends Handler update = {} update[method] = {} update[method][statName] = count or '' + console.log "... updating #{userStringID} patches #{query} to #{count}, #{usersTotal} players found so far." if count User.findByIdAndUpdate user.get('_id'), update, doneWithUser statRecalculators: gamesCompleted: (done) -> LevelSession = require '../levels/sessions/LevelSession' - userStream = User.find().stream() + userStream = User.find({anonymous: false}).sort('_id').stream() streamFinished = false usersTotal = 0 usersFinished = 0 + numberRunning = 0 doneWithUser = (err) -> log.error err if err? ++usersFinished - done?() if streamFinished and usersFinished is usersTotal + --numberRunning + userStream.resume() + if streamFinished and usersFinished is usersTotal + console.log "----------- Finished recalculating statistics for gamesCompleted for #{usersFinished} players. -----------" + done?() userStream.on 'error', (err) -> log.error err userStream.on 'close', -> streamFinished = true userStream.on 'data', (user) -> - usersTotal += 1 + ++usersTotal + ++numberRunning + userStream.pause() if numberRunning > 20 userID = user.get('_id').toHexString() - LevelSession.count {creator: userID, 'state.completed': true}, (err, count) -> + LevelSession.count {creator: userID, 'state.complete': true}, (err, count) -> update = if count then {$set: 'stats.gamesCompleted': count} else {$unset: 'stats.gamesCompleted': ''} + console.log "... updating #{userID} gamesCompleted to #{count}, #{usersTotal} players found so far." if Math.random() < 0.001 User.findByIdAndUpdate user.get('_id'), update, doneWithUser articleEdits: (done) -> diff --git a/server_config.coffee b/server_config.coffee index 20cb28341..dbb910f27 100644 --- a/server_config.coffee +++ b/server_config.coffee @@ -2,6 +2,8 @@ config = {} config.unittest = process.argv.indexOf('--unittest') > -1 +config.tokyo = process.env.TOKYO or false +config.chinaDomain = "http://cn.codecombat.com" config.port = process.env.COCO_PORT or process.env.COCO_NODE_PORT or 3000 config.ssl_port = process.env.COCO_SSL_PORT or process.env.COCO_SSL_NODE_PORT or 3443 config.cloudflare = @@ -15,11 +17,20 @@ config.mongo = port: process.env.COCO_MONGO_PORT or 27017 host: process.env.COCO_MONGO_HOST or 'localhost' db: process.env.COCO_MONGO_DATABASE_NAME or 'coco' + analytics_port: process.env.COCO_MONGO_ANALYTICS_PORT or 27017 + analytics_host: process.env.COCO_MONGO_ANALYTICS_HOST or 'localhost' + analytics_db: process.env.COCO_MONGO_ANALYTICS_DATABASE_NAME or 'analytics' mongoose_replica_string: process.env.COCO_MONGO_MONGOOSE_REPLICA_STRING or '' + mongoose_tokyo_replica_string: process.env.COCO_MONGO_MONGOOSE_TOKYO_REPLICA_STRING or '' + +if config.tokyo + config.mongo.readpref = 'nearest' +else + config.mongo.readpref = 'primary' config.apple = verifyURL: process.env.COCO_APPLE_VERIFY_URL or 'https://sandbox.itunes.apple.com/verifyReceipt' - + config.stripe = secretKey: process.env.COCO_STRIPE_SECRET_KEY or 'sk_test_MFnZHYD0ixBbiBuvTlLjl2da' @@ -36,9 +47,10 @@ else config.mongo.password = process.env.COCO_MONGO_PASSWORD or '' config.mail = - service: process.env.COCO_MAIL_SERVICE_NAME or 'Zoho' username: process.env.COCO_MAIL_SERVICE_USERNAME or '' - password: process.env.COCO_MAIL_SERVICE_PASSWORD or '' + supportPrimary: process.env.COCO_MAIL_SUPPORT_PRIMARY or '' + supportPremium: process.env.COCO_MAIL_SUPPORT_PREMIUM or '' + username: process.env.COCO_MAIL_SERVICE_USERNAME or '' mailchimpAPIKey: process.env.COCO_MAILCHIMP_API_KEY or '' mailchimpWebhook: process.env.COCO_MAILCHIMP_WEBHOOK or '/mail/webhook' sendwithusAPIKey: process.env.COCO_SENDWITHUS_API_KEY or '' @@ -47,8 +59,11 @@ config.mail = cronHandlerPublicIP: process.env.COCO_CRON_PUBLIC_IP or '' cronHandlerPrivateIP: process.env.COCO_CRON_PRIVATE_IP or '' -config.hipchatAPIKey = process.env.COCO_HIPCHAT_API_KEY or '' -config.hipchatTowerAPIKey = process.env.COCO_HIPCHAT_TOWER_API_KEY or '' +config.hipchat = + main: process.env.COCO_HIPCHAT_API_KEY or '' + tower: process.env.COCO_HIPCHAT_TOWER_API_KEY or '' + artisans: process.env.COCO_HIPCHAT_ARTISANS_API_KEY or '' + config.queue = accessKeyId: process.env.COCO_AWS_ACCESS_KEY_ID or '' secretAccessKey: process.env.COCO_AWS_SECRET_ACCESS_KEY or '' diff --git a/server_setup.coffee b/server_setup.coffee index a3f972caa..9c3283acf 100644 --- a/server_setup.coffee +++ b/server_setup.coffee @@ -5,6 +5,7 @@ useragent = require 'express-useragent' fs = require 'graceful-fs' log = require 'winston' compressible = require 'compressible' +geoip = require 'geoip-lite' database = require './server/commons/database' baseRoute = require './server/routes/base' @@ -16,6 +17,8 @@ UserHandler = require './server/users/user_handler' hipchat = require './server/hipchat' global.tv4 = require 'tv4' # required for TreemaUtils to work global.jsondiffpatch = require 'jsondiffpatch' +global.stripe = require('stripe')(config.stripe.secretKey) + productionLogging = (tokens, req, res) -> status = res.statusCode @@ -42,23 +45,27 @@ developmentLogging = (tokens, req, res) -> setupErrorMiddleware = (app) -> app.use (err, req, res, next) -> if err - res.status(500).send(error: "Something went wrong!") + if err.status and 400 <= err.status < 500 + res.status(err.status).send("Error #{err.status}") + return + res.status(err.status ? 500).send(error: "Something went wrong!") message = "Express error: #{req.method} #{req.path}: #{err.message}" log.error "#{message}, stack: #{err.stack}" - hipchat.sendTowerHipChatMessage(message) + hipchat.sendHipChatMessage(message, ['tower'], {papertrail: true}) else next(err) + setupExpressMiddleware = (app) -> if config.isProduction express.logger.format('prod', productionLogging) app.use(express.logger('prod')) app.use express.compress filter: (req, res) -> - return false if req.headers.host is 'codecombat.com' # CloudFlare will gzip it for us on codecombat.com # But now it's disabled. + return false if req.headers.host is 'codecombat.com' # CloudFlare will gzip it for us on codecombat.com compressible res.getHeader('Content-Type') else express.logger.format('dev', developmentLogging) app.use(express.logger('dev')) - app.use(express.static(path.join(__dirname, 'public'))) + app.use(express.static(path.join(__dirname, 'public'), maxAge: 0)) # CloudFlare overrides maxAge, and we don't want local development caching. app.use(useragent.express()) app.use(express.favicon()) @@ -71,6 +78,29 @@ setupPassportMiddleware = (app) -> app.use(authentication.initialize()) app.use(authentication.session()) +setupChinaRedirectMiddleware = (app) -> + shouldRedirectToChinaVersion = (req) -> + firstLanguage = req.acceptedLanguages[0] + speaksChinese = firstLanguage and firstLanguage.indexOf('zh') isnt -1 + unless config.tokyo + ip = req.headers['x-forwarded-for'] or req.connection.remoteAddress + ip = ip?.split(' ')[0] # If there are two IP addresses, say because of CloudFlare, we just take the first. + geo = geoip.lookup(ip) + if speaksChinese or geo?.country is "CN" + log.info("Should we redirect to Tokyo server? speaksChinese: #{speaksChinese}, firstLanguage: #{firstLanguage}, ip: #{ip}, geo: #{geo} -- so redirecting? #{geo?.country is 'CN' and speaksChinese}") + return geo?.country is "CN" and speaksChinese + else + log.info("We are on Tokyo server. speaksChinese: #{speaksChinese}, acceptedLanguages: #{req.acceptedLanguages[0]}") + req.chinaVersion = true if speaksChinese + return false # If the user is already redirected, don't redirect them! + + app.use (req, res, next) -> + if shouldRedirectToChinaVersion req + res.writeHead 302, "Location": config.chinaDomain + req.url + res.end() + else + next() + setupOneSecondDelayMiddleware = (app) -> if(config.slow_down) app.use((req, res, next) -> setTimeout((-> next()), 1000)) @@ -105,6 +135,7 @@ setupTrailingSlashRemovingMiddleware = (app) -> next() exports.setupMiddleware = (app) -> + setupChinaRedirectMiddleware app setupMiddlewareToSendOldBrowserWarningWhenPlayersViewLevelDirectly app setupExpressMiddleware app setupPassportMiddleware app @@ -122,25 +153,15 @@ setupJavascript404s = (app) -> setupFallbackRouteToIndex = (app) -> app.all '*', (req, res) -> - if req.user - sendMain(req, res) - req.user.set('lastIP', req.connection.remoteAddress) - req.user.save() - else - user = auth.makeNewUser(req) - makeNext = (req, res) -> -> sendMain(req, res) - next = makeNext(req, res) - auth.loginUser(req, res, user, false, next) - -sendMain = (req, res) -> - fs.readFile path.join(__dirname, 'public', 'main.html'), 'utf8', (err, data) -> - log.error "Error modifying main.html: #{err}" if err - # insert the user object directly into the html so the application can have it immediately. Sanitize </script> - data = data.replace('"userObjectTag"', JSON.stringify(UserHandler.formatEntity(req, req.user)).replace(/\//g, '\\/')) - res.header 'Cache-Control', 'no-cache, no-store, must-revalidate' - res.header 'Pragma', 'no-cache' - res.header 'Expires', 0 - res.send 200, data + fs.readFile path.join(__dirname, 'public', 'main.html'), 'utf8', (err, data) -> + log.error "Error modifying main.html: #{err}" if err + # insert the user object directly into the html so the application can have it immediately. Sanitize </script> + user = if req.user then JSON.stringify(UserHandler.formatEntity(req, req.user)).replace(/\//g, '\\/') else '{}' + data = data.replace('"userObjectTag"', user) + res.header 'Cache-Control', 'no-cache, no-store, must-revalidate' + res.header 'Pragma', 'no-cache' + res.header 'Expires', 0 + res.send 200, data setupFacebookCrossDomainCommunicationRoute = (app) -> app.get '/channel.html', (req, res) -> diff --git a/test/app/lib/deltas.spec.coffee b/test/app/core/deltas.spec.coffee similarity index 94% rename from test/app/lib/deltas.spec.coffee rename to test/app/core/deltas.spec.coffee index e658f3869..a08e433cf 100644 --- a/test/app/lib/deltas.spec.coffee +++ b/test/app/core/deltas.spec.coffee @@ -1,4 +1,4 @@ -deltas = require 'lib/deltas' +deltas = require 'core/deltas' describe 'deltas lib', -> diff --git a/test/app/lib/FacebookHandler.spec.coffee b/test/app/core/social-handlers/FacebookHandler.spec.coffee similarity index 97% rename from test/app/lib/FacebookHandler.spec.coffee rename to test/app/core/social-handlers/FacebookHandler.spec.coffee index 5084800c1..6f896d3eb 100644 --- a/test/app/lib/FacebookHandler.spec.coffee +++ b/test/app/core/social-handlers/FacebookHandler.spec.coffee @@ -1,4 +1,4 @@ -FacebookHandler = require 'lib/FacebookHandler' +FacebookHandler = require 'core/social-handlers/FacebookHandler' mockAuthEvent = response: diff --git a/test/app/lib/utils.spec.coffee b/test/app/core/utils.spec.coffee similarity index 98% rename from test/app/lib/utils.spec.coffee rename to test/app/core/utils.spec.coffee index bb1ab7233..048744eb4 100644 --- a/test/app/lib/utils.spec.coffee +++ b/test/app/core/utils.spec.coffee @@ -1,5 +1,5 @@ describe 'Utility library', -> - util = require 'lib/utils' + util = require 'core/utils' describe 'i18n', -> beforeEach -> diff --git a/test/app/lib/LevelLoader.spec.coffee b/test/app/lib/LevelLoader.spec.coffee index f177c703f..b7416abf5 100644 --- a/test/app/lib/LevelLoader.spec.coffee +++ b/test/app/lib/LevelLoader.spec.coffee @@ -119,7 +119,7 @@ describe 'LevelLoader', -> # first load Tharin by the 'normal' session load responses = '/db/level/id': levelWithOgreWithMace jasmine.Ajax.requests.sendResponses(responses) - responses = '/db/level_session/id': sessionWithTharinWithHelmet + responses = '/db/level.session/id': sessionWithTharinWithHelmet jasmine.Ajax.requests.sendResponses(responses) # then try to load Tharin some more diff --git a/test/app/models/CocoModel.spec.coffee b/test/app/models/CocoModel.spec.coffee index 0cce0b1cd..ff9181a97 100644 --- a/test/app/models/CocoModel.spec.coffee +++ b/test/app/models/CocoModel.spec.coffee @@ -1,5 +1,5 @@ CocoModel = require 'models/CocoModel' -utils = require 'lib/utils' +utils = require 'core/utils' class BlandClass extends CocoModel @className: 'Bland' @@ -93,7 +93,7 @@ describe 'CocoModel', -> b.patch() request = jasmine.Ajax.requests.mostRecent() attrs = JSON.stringify(b.attributes) # server responds with all - request.response({status: 200, responseText: attrs}) + request.respondWith({status: 200, responseText: attrs}) b.set('number', 3) b.patch() @@ -125,7 +125,7 @@ describe 'CocoModel', -> b = new BlandClass({}) res = b.save() request = jasmine.Ajax.requests.mostRecent() - request.response(status: 200, responseText: '{}') + request.respondWith(status: 200, responseText: '{}') collection = [] model = @@ -140,7 +140,7 @@ describe 'CocoModel', -> ready true else return ready false - request.response {status: 200, responseText: JSON.stringify collection} + request.respondWith {status: 200, responseText: JSON.stringify collection} utils.keepDoingUntil (ready) -> request = jasmine.Ajax.requests.mostRecent() @@ -149,7 +149,7 @@ describe 'CocoModel', -> ready true else return ready false - request.response {status:200, responseText: JSON.stringify me} + request.respondWith {status:200, responseText: JSON.stringify me} describe 'updateI18NCoverage', -> class FlexibleClass extends CocoModel diff --git a/test/app/models/SuperModel.spec.coffee b/test/app/models/SuperModel.spec.coffee index 12219a584..b900ff9c7 100644 --- a/test/app/models/SuperModel.spec.coffee +++ b/test/app/models/SuperModel.spec.coffee @@ -46,7 +46,7 @@ describe 'SuperModel', -> s.once 'loaded-all', -> triggered = true s.loadModel(m, 'user') request = jasmine.Ajax.requests.mostRecent() - request.response({status: 200, responseText: '{}'}) + request.respondWith({status: 200, responseText: '{}'}) _.defer -> expect(triggered).toBe(true) done() diff --git a/test/app/views/modal/AuthModal.spec.coffee b/test/app/views/core/AuthModal.spec.coffee similarity index 71% rename from test/app/views/modal/AuthModal.spec.coffee rename to test/app/views/core/AuthModal.spec.coffee index 199b265f4..698361dc6 100644 --- a/test/app/views/modal/AuthModal.spec.coffee +++ b/test/app/views/core/AuthModal.spec.coffee @@ -1,9 +1,9 @@ -AuthModal = require 'views/modal/AuthModal' -RecoverModal = require 'views/modal/RecoverModal' +AuthModal = require 'views/core/AuthModal' +RecoverModal = require 'views/core/RecoverModal' describe 'AuthModal', -> it 'opens the recover modal when you click the recover link', -> - m = new AuthModal() + m = new AuthModal({mode: 'login'}) m.render() spyOn(m, 'openModalView') m.$el.find('#link-to-recover').click() diff --git a/test/app/views/editor/component/ThangComponentsEditView.spec.coffee b/test/app/views/editor/component/ThangComponentsEditView.spec.coffee index a2349a8d9..49c31cdf7 100644 --- a/test/app/views/editor/component/ThangComponentsEditView.spec.coffee +++ b/test/app/views/editor/component/ThangComponentsEditView.spec.coffee @@ -34,7 +34,6 @@ describe 'ThangComponentsEditView', -> supermodel = new SuperModel() supermodel.registerModel(componentC) view = new ThangComponentEditView({ components: [], supermodel: supermodel }) - jasmine.Ajax.requests.sendResponses { '/db/thang.type': [] } _.defer -> view.render() view.componentsTreema.set('/', [ { original: 'C', majorVersion: 0 }]) diff --git a/test/app/views/editor/level/EditorLevelView.spec.coffee b/test/app/views/editor/level/EditorLevelView.spec.coffee index 7a9064993..35beea837 100644 --- a/test/app/views/editor/level/EditorLevelView.spec.coffee +++ b/test/app/views/editor/level/EditorLevelView.spec.coffee @@ -7,7 +7,7 @@ describe 'LevelEditView', -> it 'opens just one modal when you click it', -> view = new LevelEditView({}, 'something') request = jasmine.Ajax.requests.first() - request.response {status: 200, responseText: JSON.stringify(emptyLevel)} + request.respondWith {status: 200, responseText: JSON.stringify(emptyLevel)} view.render() spyOn(view, 'openModalView') view.$el.find('#revert-button').click() diff --git a/test/app/views/play/ladder/LadderTabView.coffee b/test/app/views/play/ladder/LadderTabView.coffee index e23fc26be..0fe946224 100644 --- a/test/app/views/play/ladder/LadderTabView.coffee +++ b/test/app/views/play/ladder/LadderTabView.coffee @@ -1,4 +1,4 @@ -LadderTabView = require 'views/play/ladder/LadderTabView' +LadderTabView = require 'views/ladder/LadderTabView' Level = require 'models/Level' fixtures = require 'test/app/fixtures/levels' @@ -14,5 +14,5 @@ describe 'LeaderboardData', -> request = jasmine.Ajax.requests.mostRecent() triggered = false leaderboard.once 'sync', -> triggered = true - request.response({status: 200, responseText: '{}'}) + request.respondWith({status: 200, responseText: '{}'}) expect(triggered).toBe(true) diff --git a/test/server/common.coffee b/test/server/common.coffee index 66fe5f40e..2862027bb 100644 --- a/test/server/common.coffee +++ b/test/server/common.coffee @@ -4,10 +4,13 @@ console.log 'IT BEGINS' require 'jasmine-spec-reporter' +jasmine.getEnv().defaultTimeoutInterval = 300000 jasmine.getEnv().reporter.subReporters_ = [] jasmine.getEnv().addReporter(new jasmine.SpecReporter({ - displaySuccessfulSpec: true, displayFailedSpec: true + displayPendingSpec: true + displaySpecDuration: true + displaySuccessfulSpec: true })) rep = new jasmine.JsApiReporter() @@ -21,10 +24,12 @@ mongoose.connect('mongodb://localhost/coco_unittest') path = require 'path' GLOBAL.testing = true GLOBAL.tv4 = require 'tv4' # required for TreemaUtils to work -# _.str = require 'underscore.string' models_path = [ + '../../server/analytics/AnalyticsUsersActive' '../../server/articles/Article' + '../../server/campaigns/Campaign' + '../../server/clans/Clan' '../../server/levels/Level' '../../server/levels/components/LevelComponent' '../../server/levels/systems/LevelSystem' @@ -35,6 +40,8 @@ models_path = [ '../../server/achievements/Achievement' '../../server/achievements/EarnedAchievement' '../../server/payments/Payment' + '../../server/prepaids/Prepaid' + '../../server/trial_requests/TrialRequest' ] for m in models_path @@ -111,6 +118,30 @@ wrapUpGetUser = (email, user, done) -> GLOBAL.getURL = (path) -> return 'http://localhost:3001' + path +GLOBAL.createPrepaid = (type, done) -> + options = uri: GLOBAL.getURL('/db/prepaid/-/create') + options.json = type: type if type? + request.post options, done + +newUserCount = 0 +GLOBAL.createNewUser = (done) -> + name = password = "user#{newUserCount++}" + email = "#{name}@foo.bar" + unittest.getUser name, email, password, done, true +GLOBAL.loginNewUser = (done) -> + name = password = "user#{newUserCount++}" + email = "#{name}@me.com" + request.post getURL('/auth/logout'), -> + unittest.getUser name, email, password, (user) -> + req = request.post(getURL('/auth/login'), (error, response) -> + expect(response.statusCode).toBe(200) + done(user) + ) + form = req.form() + form.append('username', email) + form.append('password', password) + , true + GLOBAL.loginJoe = (done) -> request.post getURL('/auth/logout'), -> unittest.getNormalJoe (user) -> @@ -145,6 +176,20 @@ GLOBAL.loginAdmin = (done) -> form.append('password', '80yqxpb38j') # find some other way to make the admin object an admin... maybe directly? +GLOBAL.loginUser = (user, done) -> + request.post getURL('/auth/logout'), -> + req = request.post(getURL('/auth/login'), (error, response) -> + expect(response.statusCode).toBe(200) + done(user) + ) + form = req.form() + form.append('username', user.get('email')) + form.append('password', user.get('name')) + +GLOBAL.logoutUser = (done) -> + request.post getURL('/auth/logout'), -> + done() + GLOBAL.dropGridFS = (done) -> if mongoose.connection.readyState is 2 mongoose.connection.once 'open', -> diff --git a/test/server/functional/achievement.spec.coffee b/test/server/functional/achievement.spec.coffee index bb314caa6..9d677cd07 100644 --- a/test/server/functional/achievement.spec.coffee +++ b/test/server/functional/achievement.spec.coffee @@ -21,6 +21,8 @@ repeatable = userField: '_id' proportionalTo: 'simulatedBy' recalculable: true + rewards: + gems: 1 diminishing = name: 'Simulated2' @@ -169,19 +171,23 @@ describe 'Achieving Achievements', -> it 'verify that a repeatable achievement has been earned', (done) -> unittest.getNormalJoe (joe) -> - EarnedAchievement.find {achievementName: repeatable.name}, (err, docs) -> - expect(err).toBeNull() - expect(docs.length).toBe(1) - achievement = docs[0] - if achievement - expect(achievement.get 'achievement').toBe repeatable._id - expect(achievement.get 'user').toBe joe._id.toHexString() - expect(achievement.get 'notified').toBeFalsy() - expect(achievement.get 'earnedPoints').toBe 2 * repeatable.worth - expect(achievement.get 'achievedAmount').toBe 2 - expect(achievement.get 'previouslyAchievedAmount').toBeFalsy() - done() + User.findById(joe.get('_id')).exec (err, joe2) -> + expect(joe2.get('earned').gems).toBe(2) + + EarnedAchievement.find {achievementName: repeatable.name}, (err, docs) -> + expect(err).toBeNull() + expect(docs.length).toBe(1) + achievement = docs[0] + + if achievement + expect(achievement.get 'achievement').toBe repeatable._id + expect(achievement.get 'user').toBe joe._id.toHexString() + expect(achievement.get 'notified').toBeFalsy() + expect(achievement.get 'earnedPoints').toBe 2 * repeatable.worth + expect(achievement.get 'achievedAmount').toBe 2 + expect(achievement.get 'previouslyAchievedAmount').toBeFalsy() + done() it 'verify that the repeatable achievement with complex exp has been earned', (done) -> unittest.getNormalJoe (joe) -> @@ -196,6 +202,18 @@ describe 'Achieving Achievements', -> done() + it 'increases gems proportionally to changes made', (done) -> + unittest.getNormalJoe (joe) -> + User.findById(joe.get('_id')).exec (err, joe2) -> + joe2.set('simulatedBy', 4) + expect(joe2.get('earned').gems).toBe(2) + joe2.save (err, joe3) -> + expect(err).toBeNull() + User.findById(joe3.get('_id')).exec (err, joe4) -> + #expect(joe4.get('earned').gems).toBe(4) # ... this sometimes gives 4, sometimes 2. Race condition? TODO + done() + + describe 'Recalculate Achievements', -> EarnedAchievementHandler = require '../../../server/achievements/earned_achievement_handler' @@ -241,7 +259,8 @@ describe 'Recalculate Achievements', -> unittest.getNormalJoe (joe) -> User.findById joe.get('id'), (err, guy) -> expect(err).toBeNull() - expect(guy.get 'points').toBe unlockable.worth + 2 * repeatable.worth + (Math.log(.5 * (2 + .5)) + 1) * diminishing.worth + expect(guy.get 'points').toBe unlockable.worth + 4 * repeatable.worth + (Math.log(.5 * (4 + .5)) + 1) * diminishing.worth + expect(guy.get('earned').gems).toBe 4 * repeatable.rewards.gems done() it 'cleaning up test: deleting all Achievements and related', (done) -> diff --git a/test/server/functional/campaign_handler.spec.coffee b/test/server/functional/campaign_handler.spec.coffee new file mode 100644 index 000000000..35a341e86 --- /dev/null +++ b/test/server/functional/campaign_handler.spec.coffee @@ -0,0 +1,76 @@ +require '../common' + +levels = [ + { + name: 'Level 1' + description: 'This is the first level.' + disableSpaces: true + icon: 'somestringyoudontneed.png' + } + { + name: 'Level 2' + description: 'This is the second level.' + requiresSubscription: true + backspaceThrottle: true + } +] + +achievement = { + name: 'Level 1 Complete' +} + +campaign = { + name: 'Campaign' + levels: {} +} + +levelURL = getURL('/db/level') +achievementURL = getURL('/db/achievement') +campaignURL = getURL('/db/campaign') +campaignSchema = require '../../../app/schemas/models/campaign.schema' +campaignLevelProperties = _.keys(campaignSchema.properties.levels.additionalProperties.properties) + +describe '/db/campaign', -> + it 'prepares the db first', (done) -> + clearModels [Achievement, Campaign, Level, User], (err) -> + expect(err).toBeNull() + loginAdmin (admin) -> + levels[0].permissions = levels[1].permissions = [{target: admin._id, access: 'owner'}] + request.post {uri: levelURL, json: levels[0]}, (err, res, body) -> + expect(res.statusCode).toBe(200) + levels[0] = body + request.post {uri: levelURL, json: levels[1]}, (err, res, body) -> + expect(res.statusCode).toBe(200) + levels[1] = body + achievement.related = levels[0].original + achievement.rewards = { levels: [levels[1].original] } + request.post {uri: achievementURL, json: achievement}, (err, res, body) -> + achievement = body + done() + + it 'can create campaigns', (done) -> + for level in levels.reverse() + campaign.levels[level.original] = _.pick level, campaignLevelProperties + request.post {uri: campaignURL, json: campaign}, (err, res, body) -> + expect(res.statusCode).toBe(200) + campaign = body + done() + +describe '/db/campaign/.../levels', -> + it 'fetches the levels in a campaign', (done) -> + url = getURL("/db/campaign/#{campaign._id}/levels") + request.get {uri: url}, (err, res, body) -> + expect(res.statusCode).toBe(200) + body = JSON.parse(body) + expect(body.length).toBe(2) + expect(_.difference(['level-1', 'level-2'],(level.slug for level in body)).length).toBe(0) + done() + +describe '/db/campaign/.../achievements', -> + it 'fetches the achievements in the levels in a campaign', (done) -> + url = getURL("/db/campaign/#{campaign._id}/achievements") + request.get {uri: url}, (err, res, body) -> + expect(res.statusCode).toBe(200) + body = JSON.parse(body) + expect(body.length).toBe(1) + done() diff --git a/test/server/functional/clan.spec.coffee b/test/server/functional/clan.spec.coffee new file mode 100644 index 000000000..b608da92a --- /dev/null +++ b/test/server/functional/clan.spec.coffee @@ -0,0 +1,509 @@ +config = require '../../../server_config' +require '../common' +utils = require '../../../app/core/utils' # Must come after require /common +mongoose = require 'mongoose' + +describe 'Clans', -> + clanURL = getURL('/db/clan') + userURL = getURL('/db/user') + + clanCount = 0 + createClanName = (name) -> name + clanCount++ + + createClan = (user, type, description, done) -> + name = createClanName 'myclan' + requestBody = + type: type + name: name + requestBody.description = description if description? + request.post {uri: clanURL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + expect(body.type).toEqual(type) + expect(body.name).toEqual(name) + expect(body.description).toEqual(description) if description? + expect(body.dashboardType).toEqual('premium') if type is 'private' + expect(body.members?.length).toEqual(1) + expect(body.members?[0]).toEqual(user.id) + Clan.findById body._id, (err, clan) -> + expect(clan.get('type')).toEqual(type) + expect(clan.get('name')).toEqual(name) + expect(clan.get('description')).toEqual(description) if description? + expect(clan.get('dashboardType')).toEqual('premium') if type is 'private' + expect(clan.get('members')?.length).toEqual(1) + expect(clan.get('members')?[0]).toEqual(user._id) + User.findById user.id, (err, user) -> + expect(err).toBeNull() + expect(user.get('clans')?.length).toBeGreaterThan(0) + expect(_.find user.get('clans'), (clanID) -> clan._id.equals clanID).toBeDefined() + done(clan) + + it 'Clear database users and clans', (done) -> + clearModels [User, Clan], (err) -> + throw err if err + done() + + describe 'Public', -> + + it 'Create clan', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', 'test description', (clan) -> + done() + + it 'Anonymous create clan 401', (done) -> + logoutUser -> + requestBody = + type: 'public' + name: createClanName 'myclan' + request.post {uri: clanURL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(401) + done() + + it 'Create clan missing type 422', (done) -> + loginNewUser (user1) -> + requestBody = + name: createClanName 'myclan' + request.post {uri: clanURL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(422) + done() + + it 'Create clan missing name 422', (done) -> + loginNewUser (user1) -> + requestBody = + type: 'public' + request.post {uri: clanURL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(422) + done() + + it 'Edit clan name', (done) -> + newName = 'new clan name' + loginNewUser (user1) -> + createClan user1, 'public', 'test description', (clan) -> + requestBody = clan.toObject() + requestBody.name = newName + request.put {uri: clanURL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(body.name).toEqual(newName) + Clan.findById clan.id, (err, clan) -> + expect(err).toBeNull() + expect(clan.get('name')).toEqual(newName) + done() + + it 'Edit clan name, not owner 403', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', 'test description', (clan) -> + oldName = clan.get('name') + loginNewUser (user2) -> + requestBody = clan.toObject() + requestBody.name = 'new clan name' + request.put {uri: clanURL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toEqual(403) + Clan.findById clan.id, (err, clan) -> + expect(err).toBeNull() + expect(clan.get('name')).toEqual(oldName) + done() + + it 'Edit clan description', (done) -> + newDescription = 'new description' + loginNewUser (user1) -> + createClan user1, 'public', 'test description', (clan) -> + requestBody = clan.toObject() + requestBody.description = newDescription + request.put {uri: clanURL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(body.description).toEqual(newDescription) + Clan.findById clan.id, (err, clan) -> + expect(err).toBeNull() + expect(clan.get('description')).toEqual(newDescription) + done() + + it 'Edit clan description, not owner 403', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', 'test description', (clan) -> + oldDescription = clan.get('description') + loginNewUser (user2) -> + requestBody = clan.toObject() + requestBody.description = 'new description' + request.put {uri: clanURL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toEqual(403) + Clan.findById clan.id, (err, clan) -> + expect(err).toBeNull() + expect(clan.get('description')).toEqual(oldDescription) + done() + + it 'Get public clans', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + createClan user1, 'public', 'the second clan', (clan2) -> + request.get {uri: "#{clanURL}/-/public" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + expect(body.length).toBeGreaterThan(1) + done() + + it 'Get public clans anonymous', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + createClan user1, 'public', null, (clan2) -> + logoutUser -> + request.get {uri: "#{clanURL}/-/public" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + expect(body.length).toBeGreaterThan(1) + done() + + it 'Join clan', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + loginNewUser (user2) -> + request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + Clan.findById clan1.id, (err, clan1) -> + expect(err).toBeNull() + expect(clan1.get('members')?.length).toEqual(2) + expect(_.find clan1.get('members'), (memberID) -> user2._id.equals memberID).toBeDefined() + User.findById user2.id, (err, user2) -> + expect(err).toBeNull() + expect(user2.get('clans')?.length).toBeGreaterThan(0) + expect(_.find user2.get('clans'), (clanID) -> clan1._id.equals clanID).toBeDefined() + done() + + it 'Join invalid clan 404', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + loginNewUser (user2) -> + request.put {uri: "#{clanURL}/1234/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(404) + done() + + it 'Join clan anonymous 401', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + logoutUser -> + request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(401) + done() + + it 'Join clan twice 200', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + loginNewUser (user2) -> + request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + Clan.findById clan1.id, (err, clan1) -> + expect(err).toBeNull() + expect(_.find clan1.get('members'), (memberID) -> memberID.equals user2.id).toBeDefined() + request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + done() + + it 'Leave clan', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', 'do not stay too long', (clan1) -> + loginNewUser (user2) -> + request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + request.put {uri: "#{clanURL}/#{clan1.id}/leave" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + Clan.findById clan1.id, (err, clan1) -> + expect(err).toBeNull() + expect(_.find clan1.get('members'), (memberID) -> memberID.equals user2.id).toBeUndefined() + User.findById user2.id, (err, user2) -> + expect(err).toBeNull() + expect(user2.get('clans').length).toEqual(0) + done() + + it 'Leave clan not member 200', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + loginNewUser (user2) -> + request.put {uri: "#{clanURL}/#{clan1.id}/leave" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + Clan.findById clan1.id, (err, clan1) -> + expect(err).toBeNull() + expect(_.find clan1.get('members'), (memberID) -> memberID.equals user2.id).toBeUndefined() + done() + + it 'Leave owned clan 403', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + request.put {uri: "#{clanURL}/#{clan1.id}/leave" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Remove member', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + loginNewUser (user2) -> + request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + loginUser user1, (user1) -> + request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user2.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + Clan.findById clan1.id, (err, clan1) -> + expect(err).toBeNull() + expect(clan1.get('members').length).toEqual(1) + expect(clan1.get('members')[0]).toEqual(user1.get('_id')) + User.findById user2.id, (err, user2) -> + expect(err).toBeNull() + expect(user2.get('clans').length).toEqual(0) + done() + + it 'Remove non-member 200', (done) -> + loginNewUser (user2) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user2.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + Clan.findById clan1.id, (err, clan1) -> + expect(err).toBeNull() + expect(clan1.get('members').length).toEqual(1) + expect(clan1.get('members')[0]).toEqual(user1.get('_id')) + done() + + it 'Remove invalid memberID 404', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + request.put {uri: "#{clanURL}/#{clan1.id}/remove/123" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(404) + done() + + it 'Remove member, not in clan 403', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + loginNewUser (user2) -> + request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + loginNewUser (user3) -> + request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user2.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Remove member, not the owner 403', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + loginNewUser (user2) -> + request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + loginNewUser (user3) -> + request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user2.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Remove member from owned clan 403', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user1.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Delete clan', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan) -> + request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(204) + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + expect(user1.get('clans').length).toEqual(0) + done() + + it 'Delete clan anonymous 401', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan) -> + logoutUser -> + request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(401) + done() + + it 'Delete clan not owner 403', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan) -> + loginNewUser (user2) -> + request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Delete clan no longer exists 404', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan) -> + request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(204) + request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(404) + done() + + it 'Delete clan invalid ID 404', (done) -> + loginNewUser (user1) -> + request.del {uri: "#{clanURL}/1234" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(404) + done() + + describe 'Private', -> + # Using stripe.free = true to convert users to premium + + it 'Create clan', (done) -> + loginNewUser (user1) -> + user1.set 'stripe.free', true + user1.save (err) -> + expect(err).toBeNull() + createClan user1, 'private', 'test description', (clan) -> + done() + + it 'Create clan when not premium 403', (done) -> + loginNewUser (user1) -> + expect(user1.isPremium()).toEqual(false) + requestBody = + type: 'private' + name: createClanName 'myclan' + request.post {uri: clanURL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Join clan', (done) -> + loginNewUser (user1) -> + user1.set 'stripe.free', true + user1.save (err) -> + expect(err).toBeNull() + createClan user1, 'private', 'test description', (clan) -> + loginNewUser (user2) -> + user2.set 'stripe.free', true + user2.save (err) -> + request.put {uri: "#{clanURL}/#{clan.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + done() + + it 'Join clan when not premium 403', (done) -> + loginNewUser (user1) -> + user1.set 'stripe.free', true + user1.save (err) -> + expect(err).toBeNull() + createClan user1, 'private', 'test description', (clan) -> + loginNewUser (user2) -> + user2.save (err) -> + request.put {uri: "#{clanURL}/#{clan.id}/join" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Get public clans after creating a private clan', (done) -> + loginNewUser (user1) -> + createClan user1, 'public', null, (clan1) -> + user1.set 'stripe.free', true + user1.save (err) -> + createClan user1, 'private', 'my private clan', (clan2) -> + request.get {uri: "#{clanURL}/-/public" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + clans = JSON.parse(body) + expect(clans.length).toBeGreaterThan(1) + for clan in clans + expect(clan._id).not.toEqual(clan2.id) + done() + + it "Getting nother user's clans excludes their private ones", (done) -> + loginNewUser (user1) -> + user1.set 'stripe.free', true + user1.save (err) -> + expect(err).toBeNull() + createClan user1, 'private', 'my private clan', (clan1) -> + createClan user1, 'public', 'my public clan', (clan2) -> + loginNewUser (user2) -> + request.get {uri: "#{userURL}/#{user1.id}/clans" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + clans = JSON.parse(body) + expect(clans.length).toEqual(1) + for clan in clans + expect(clan._id).toEqual(clan2.id) + expect(clan.type).toEqual('public') + done() + + it "Getting own clans includes private ones", (done) -> + loginNewUser (user1) -> + user1.set 'stripe.free', true + user1.save (err) -> + expect(err).toBeNull() + createClan user1, 'private', 'my private clan', (clan1) -> + createClan user1, 'public', 'my public clan', (clan2) -> + request.get {uri: "#{userURL}/#{user1.id}/clans" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + clans = JSON.parse(body) + expect(clans.length).toEqual(2) + for clan in clans + if clan.type is 'public' + expect(clan._id).toEqual(clan2.id) + else + expect(clan._id).toEqual(clan1.id) + expect(clan.type).toEqual('private') + done() + + it "Can get another user's private clan", (done) -> + loginNewUser (user1) -> + user1.set 'stripe.free', true + user1.save (err) -> + expect(err).toBeNull() + createClan user1, 'private', 'my private clan', (clan1) -> + loginNewUser (user2) -> + request.get {uri: "#{clanURL}/#{clan1.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + clan = JSON.parse(body) + expect(clan._id).toEqual(clan1.id) + expect(clan.name).toEqual(clan1.get('name')) + expect(clan.type).toEqual('private') + expect(clan1.get('ownerID').equals clan.ownerID).toEqual(true) + expect(clan.description).toEqual(clan1.get('description')) + done() + + it "Can get another user's private clan as anonymous", (done) -> + loginNewUser (user1) -> + user1.set 'stripe.free', true + user1.save (err) -> + expect(err).toBeNull() + createClan user1, 'private', 'my private clan', (clan1) -> + logoutUser -> + request.get {uri: "#{clanURL}/#{clan1.id}" }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + clan = JSON.parse(body) + expect(clan._id).toEqual(clan1.id) + expect(clan.name).toEqual(clan1.get('name')) + expect(clan.type).toEqual('private') + expect(clan1.get('ownerID').equals clan.ownerID).toEqual(true) + expect(clan.description).toEqual(clan1.get('description')) + done() diff --git a/test/server/functional/discount_handler.spec.coffee b/test/server/functional/discount_handler.spec.coffee new file mode 100644 index 000000000..9cca335bc --- /dev/null +++ b/test/server/functional/discount_handler.spec.coffee @@ -0,0 +1,114 @@ +config = require '../../../server_config' +require '../common' + +# sample data that comes in through the webhook when you subscribe + + +describe '/db/user, editing stripe.couponID property', -> + + stripe = require('stripe')(config.stripe.secretKey) + userURL = getURL('/db/user') + webhookURL = getURL('/stripe/webhook') + + it 'clears the db first', (done) -> + clearModels [User, Payment], (err) -> + throw err if err + done() + + #- shared data between tests + joeData = null + firstSubscriptionID = null + + it 'does not work for non-admins', (done) -> + loginJoe (joe) -> + joeData = joe.toObject() + expect(joeData.stripe).toBeUndefined() + joeData.stripe = { couponID: '20pct' } + request.put {uri: userURL, json: joeData }, (err, res, body) -> + expect(res.statusCode).toBe(200) # fails silently + expect(res.body.stripe).toBeUndefined() # but still fails + done() + + it 'does not work with invalid coupons', (done) -> + loginAdmin (admin) -> + joeData.stripe = { couponID: 'DNE' } + request.put {uri: userURL, json: joeData }, (err, res, body) -> + expect(res.statusCode).toBe(404) + done() + + it 'sets the couponID on a user without an existing stripe object', (done) -> + joeData.stripe = { couponID: '20pct' } + request.put {uri: userURL, json: joeData }, (err, res, body) -> + expect(err).toBeNull() + return done() if err + joeData = body + expect(res.statusCode).toBe(200) + expect(body.stripe.couponID).toBe('20pct') + done() + + it 'just updates the couponID when it changes and there is no existing subscription', (done) -> + joeData.stripe.couponID = '500off' + request.put {uri: userURL, json: joeData }, (err, res, body) -> + expect(res.statusCode).toBe(200) + expect(body.stripe.couponID).toBe('500off') + done() + + it 'removes the couponID from the user when the admin makes it so', (done) -> + delete joeData.stripe.couponID + request.put {uri: userURL, json: joeData }, (err, res, body) -> + joeData = body + expect(res.statusCode).toBe(200) + expect(body.stripe).toBeUndefined() + done() + + it 'puts the coupon back', (done) -> + joeData.stripe = {couponID: '500off'} + request.put {uri: userURL, json: joeData }, (err, res, body) -> + expect(res.statusCode).toBe(200) + expect(body.stripe.couponID).toBe('500off') + done() + + it 'applies a discount to the newly created subscription when a plan is set', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + stripeTokenID = token.id + loginJoe (joe) -> + joeData.stripe.token = stripeTokenID + joeData.stripe.planID = 'basic' + request.put {uri: userURL, json: joeData, headers: {'X-Change-Plan': 'true'} }, (err, res, body) -> + joeData = body + expect(res.statusCode).toBe(200) + stripe.customers.retrieveSubscription joeData.stripe.customerID, joeData.stripe.subscriptionID, (err, subscription) -> + expect(subscription.discount).toBeDefined() + expect(subscription.discount?.coupon.id).toBe('500off') + done() + + + it 'updates the discount on the customer when an admin changes the couponID', (done) -> + loginAdmin (admin) -> + joeData.stripe.couponID = '20pct' + request.put {uri: userURL, json: joeData }, (err, res, body) -> + expect(res.statusCode).toBe(200) + expect(body.stripe.couponID).toBe('20pct') + stripe.customers.retrieve joeData.stripe.customerID, (err, customer) -> + expect(customer.discount.coupon.id).toBe('20pct') + done() + + it 'removes discounts from the customer when an admin removes the couponID', (done) -> + delete joeData.stripe.couponID + request.put {uri: userURL, json: joeData }, (err, res, body) -> + expect(res.statusCode).toBe(200) + expect(body.stripe.couponID).toBeUndefined() + stripe.customers.retrieve joeData.stripe.customerID, (err, customer) -> + expect(customer.discount).toBe(null) + done() + + it 'adds a discount to the customer when an admin adds the couponID', (done) -> + joeData.stripe.couponID = '20pct' + request.put {uri: userURL, json: joeData }, (err, res, body) -> + expect(res.statusCode).toBe(200) + expect(body.stripe.couponID).toBe('20pct') + stripe.customers.retrieve joeData.stripe.customerID, (err, customer) -> + expect(customer.discount.coupon.id).toBe('20pct') + done() diff --git a/test/server/functional/patch.spec.coffee b/test/server/functional/patch.spec.coffee index c17a81774..b1219ccb6 100644 --- a/test/server/functional/patch.spec.coffee +++ b/test/server/functional/patch.spec.coffee @@ -71,13 +71,13 @@ describe '/db/patch', -> it 'does not add duplicate watchers', (done) -> watchingURL = getURL("/db/article/#{articles[0]._id}/watch") request.put {uri: watchingURL, json: {on: true}}, (err, res, body) -> - expect(body.watchers.length).toBe(4) + expect(body.watchers.length).toBe(3) done() it 'allows removing yourself', (done) -> watchingURL = getURL("/db/article/#{articles[0]._id}/watch") request.put {uri: watchingURL, json: {on: false}}, (err, res, body) -> - expect(body.watchers.length).toBe(3) + expect(body.watchers.length).toBe(2) done() it 'allows the submitter to withdraw the pull request', (done) -> @@ -157,9 +157,3 @@ describe '/db/patch', -> Patch.findOne({}).exec (err, article) -> expect(article.get('status')).toBe 'accepted' done() - - - - - - diff --git a/test/server/functional/payment.spec.coffee b/test/server/functional/payment.spec.coffee index ec316a621..a20adb714 100644 --- a/test/server/functional/payment.spec.coffee +++ b/test/server/functional/payment.spec.coffee @@ -6,7 +6,9 @@ require '../common' describe '/db/payment', -> request = require 'request' paymentURL = getURL('/db/payment') - + checkChargesURL = getURL('/db/payment/check-stripe-charges') + customPaymentURL = getURL('/db/payment/custom') + firstApplePayment = { apple: { rawReceipt: testReceipt @@ -14,7 +16,7 @@ describe '/db/payment', -> localPrice: '$5.00' } } - + secondApplePayment = { apple: { rawReceipt: testReceipt @@ -22,7 +24,7 @@ describe '/db/payment', -> localPrice: '$10.00' } } - + paymentCreated = null it 'clears the db first', (done) -> @@ -31,24 +33,30 @@ describe '/db/payment', -> done() describe 'posting Apple IAPs', -> - + + it 'denies anonymous users trying to pay', (done) -> + request.get getURL('/auth/whoami'), -> + request.post {uri: paymentURL, json: firstApplePayment}, (err, res, body) -> + expect(res.statusCode).toBe 403 + done() + it 'creates a payment object and credits gems to the user', (done) -> loginJoe -> request.post {uri: paymentURL, json: firstApplePayment}, (err, res, body) -> paymentCreated = body?._id expect(res.statusCode).toBe 201 User.findOne({name:'Joe'}).exec(err, (err, user) -> - expect(user.get('purchased').gems).toBe(5000) + expect(user.get('purchased')?.gems).toBe(5000) done() ) - + it 'is idempotent', (done) -> loginJoe -> request.post {uri: paymentURL, json: firstApplePayment}, (err, res, body) -> expect(body._id is paymentCreated).toBe(true) expect(res.statusCode).toBe 200 User.findOne({name:'Joe'}).exec(err, (err, user) -> - expect(user.get('purchased').gems).toBe(5000) + expect(user.get('purchased')?.gems).toBe(5000) done() ) @@ -64,18 +72,18 @@ describe '/db/payment', -> expect(body._id is paymentCreated).toBe(false) expect(res.statusCode).toBe 201 User.findOne({name:'Joe'}).exec(err, (err, user) -> - expect(user.get('purchased').gems).toBe(16000) + expect(user.get('purchased')?.gems).toBe(16000) done() ) - - describe 'posting Stripe purchases', -> + describe 'posting Stripe purchases', -> stripe = require('stripe')(config.stripe.secretKey) charge = null joeID = null timestamp = new Date().getTime() stripeTokenID = null + joeData = null it 'clears the db first', (done) -> clearModels [User, Payment], (err) -> @@ -109,11 +117,11 @@ describe '/db/payment', -> expect(body.purchaser).toBe(joeID) User.findById(joe.get('_id'), (err, user) -> expect(user.get('purchased').gems).toBe(5000) - expect(user.get('stripeCustomerID')).toBe(body.stripe.customerID) + expect(user.get('stripe').customerID).toBe(body.stripe.customerID) done() ) ) - + it 'ignores repeated purchases', (done) -> data = { productID: 'gems_5', stripe: { token: stripeTokenID, timestamp: timestamp } } request.post {uri: paymentURL, json: data }, (err, res, body) -> @@ -126,6 +134,34 @@ describe '/db/payment', -> ) ) + it 'allows a new charge on the existing customer', (done) -> + data = { productID: 'gems_5', stripe: { timestamp: new Date().getTime() } } + request.post {uri: paymentURL, json: data }, (err, res, body) -> + expect(res.statusCode).toBe 201 + Payment.count {}, (err, count) -> + expect(count).toBe(2) + User.findById joeID, (err, user) -> + joeData = user.toObject() + expect(user.get('purchased').gems).toBe(10000) + done() + + it "updates the customer's card when you submit a new token", (done) -> + stripe.customers.retrieve joeData.stripe.customerID, (err, customer) -> + originalCustomerID = customer.id + originalCardID = customer.sources.data[0].id + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + data = { productID: 'gems_5', stripe: { timestamp: new Date().getTime(), token: token.id } } + request.post {uri: paymentURL, json: data }, (err, res, body) -> + expect(res.statusCode).toBe(201) + User.findById joeID, (err, user) -> + joeData = user.toObject() + expect(joeData.stripe.customerID).toBe(originalCustomerID) + stripe.customers.retrieve joeData.stripe.customerID, (err, customer) -> + expect(customer.sources.data[0].id).not.toBe(originalCardID) + done() + it 'clears the db', (done) -> clearModels [User, Payment], (err) -> throw err if err @@ -136,7 +172,7 @@ describe '/db/payment', -> card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } }, (err, token) -> - data = { + data = { productID: 'gems_5' stripe: { token: token.id, timestamp: timestamp } breakAfterCharging: true @@ -145,11 +181,11 @@ describe '/db/payment', -> loginJoe (joe) -> request.post {uri: paymentURL, json: data }, (err, res, body) -> expect(res.statusCode).toBe 500 - + data = _.omit data, 'breakAfterCharging' request.post {uri: paymentURL, json: data }, (err, res, body) -> expect(res.statusCode).toBe 201 - + Payment.count({}, (err, count) -> expect(count).toBe(1) User.findById(joe.get('_id'), (err, user) -> @@ -163,7 +199,7 @@ describe '/db/payment', -> clearModels [User, Payment], (err) -> throw err if err done() - + # Testing card numbers are here: https://stripe.com/docs/testing it 'handles card that attaches to customer but fails to be charged', (done) -> @@ -226,4 +262,176 @@ describe '/db/payment', -> done() ) - \ No newline at end of file + describe '/db/payment/check-stripe-charges', -> + stripe = require('stripe')(config.stripe.secretKey) + + it 'clears the db', (done) -> + clearModels [User, Payment], (err) -> + throw err if err + done() + + it 'finds and records charges which are not in our db', (done) -> + timestamp = new Date().getTime() + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + + data = { + productID: 'gems_5' + stripe: { token: token.id, timestamp: timestamp } + breakAfterCharging: true + } + + loginJoe (joe) -> + request.post {uri: paymentURL, json: data }, (err, res, body) -> + expect(res.statusCode).toBe 500 + + request.post { uri: checkChargesURL }, (err, res, body) -> + expect(res.statusCode).toBe 201 + Payment.count({}, (err, count) -> + expect(count).toBe(1) + User.findById(joe.get('_id'), (err, user) -> + expect(user.get('purchased').gems).toBe(5000) + done() + ) + ) + + describe '/db/payment/custom', -> + stripe = require('stripe')(config.stripe.secretKey) + + it 'clears the db', (done) -> + clearModels [User, Payment], (err) -> + throw err if err + done() + + it 'handles a custom purchase with description', (done) -> + timestamp = new Date().getTime() + amount = 5000 + description = 'A sweet Coco t-shirt' + + stripe.tokens.create({ + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + stripeTokenID = token.id + loginJoe (joe) -> + joeID = joe.get('_id') + '' + data = { + amount: amount + description: description + stripe: { + token: token.id + timestamp: timestamp + } + } + request.post {uri: customPaymentURL, json: data }, (err, res, body) -> + expect(res.statusCode).toBe 201 + expect(body.stripe.chargeID).toBeDefined() + expect(body.stripe.timestamp).toBe(timestamp) + expect(body.stripe.customerID).toBeDefined() + expect(body.description).toEqual(description) + expect(body.amount).toEqual(amount) + expect(body.productID).toBe('custom') + expect(body.service).toBe('stripe') + expect(body.recipient).toBe(joeID) + expect(body.purchaser).toBe(joeID) + User.findById(joe.get('_id'), (err, user) -> + expect(user.get('stripe').customerID).toBe(body.stripe.customerID) + + criteria = + recipient: user.get('_id') + purchaser: user.get('_id') + amount: amount + description: description + service: 'stripe' + "stripe.customerID": user.get('stripe').customerID + Payment.findOne criteria, (err, payment) -> + expect(err).toBeNull() + expect(payment).not.toBeNull() + done() + ) + ) + + it 'handles a custom purchase without description', (done) -> + timestamp = new Date().getTime() + amount = 73000 + + stripe.tokens.create({ + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + stripeTokenID = token.id + loginJoe (joe) -> + joeID = joe.get('_id') + '' + data = { + amount: amount + stripe: { + token: token.id + timestamp: timestamp + } + } + request.post {uri: customPaymentURL, json: data }, (err, res, body) -> + expect(res.statusCode).toBe 201 + expect(body.stripe.chargeID).toBeDefined() + expect(body.stripe.timestamp).toBe(timestamp) + expect(body.stripe.customerID).toBeDefined() + expect(body.amount).toEqual(amount) + expect(body.productID).toBe('custom') + expect(body.service).toBe('stripe') + expect(body.recipient).toBe(joeID) + expect(body.purchaser).toBe(joeID) + User.findById(joe.get('_id'), (err, user) -> + expect(user.get('stripe').customerID).toBe(body.stripe.customerID) + + criteria = + recipient: user.get('_id') + purchaser: user.get('_id') + amount: amount + service: 'stripe' + "stripe.customerID": user.get('stripe').customerID + Payment.findOne criteria, (err, payment) -> + expect(err).toBeNull() + expect(payment).not.toBeNull() + done() + ) + ) + + it 'handles a custom purchase with invalid amount', (done) -> + timestamp = new Date().getTime() + amount = 'free?' + + stripe.tokens.create({ + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + stripeTokenID = token.id + loginJoe (joe) -> + joeID = joe.get('_id') + '' + data = { + amount: amount + stripe: { + token: token.id + timestamp: timestamp + } + } + request.post {uri: customPaymentURL, json: data }, (err, res, body) -> + expect(res.statusCode).toBe(400) + done() + ) + + it 'handles a custom purchase with no amount', (done) -> + timestamp = new Date().getTime() + + stripe.tokens.create({ + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + stripeTokenID = token.id + loginJoe (joe) -> + joeID = joe.get('_id') + '' + data = { + stripe: { + token: token.id + timestamp: timestamp + } + } + request.post {uri: customPaymentURL, json: data }, (err, res, body) -> + expect(res.statusCode).toBe(400) + done() + ) diff --git a/test/server/functional/prepaid.spec.coffee b/test/server/functional/prepaid.spec.coffee new file mode 100644 index 000000000..4280d4423 --- /dev/null +++ b/test/server/functional/prepaid.spec.coffee @@ -0,0 +1,95 @@ +require '../common' + +describe '/db/prepaid', -> + prepaidURL = getURL('/db/prepaid') + prepaidCreateURL = getURL('/db/prepaid/-/create') + + verifyPrepaid = (user, prepaid, done) -> + expect(prepaid.creator).toEqual(user.id) + expect(prepaid.type).toEqual('subscription') + expect(prepaid.status).toEqual('active') + expect(prepaid.code).toMatch(/^\w{8}$/) + expect(prepaid.properties?.couponID).toEqual('free') + done() + + it 'Clear database users and prepaids', (done) -> + clearModels [User, Prepaid], (err) -> + throw err if err + done() + + it 'Anonymous creates prepaid code', (done) -> + createPrepaid 'subscription', (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(401) + done() + + it 'Non-admin creates prepaid code', (done) -> + loginNewUser (user1) -> + expect(user1.isAdmin()).toEqual(false) + createPrepaid 'subscription', (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Admin creates prepaid code with type subscription', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + createPrepaid 'subscription', (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + verifyPrepaid user1, body, done + + it 'Admin creates prepaid code with invalid type', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + createPrepaid 'bulldozer', (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Admin creates prepaid code with no type specified', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + createPrepaid null, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Non-admin requests /db/prepaid', (done) -> + loginNewUser (user1) -> + expect(user1.isAdmin()).toEqual(false) + request.get {uri: prepaidURL}, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Admin requests /db/prepaid', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + createPrepaid 'subscription', (err, res, prepaid) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + request.get {uri: prepaidURL}, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + prepaids = JSON.parse(body) + found = false + for p in prepaids + if p._id is prepaid._id + found = true + verifyPrepaid user1, p, done + break + expect(found).toEqual(true) + done() unless found diff --git a/test/server/functional/subscription.spec.coffee b/test/server/functional/subscription.spec.coffee new file mode 100644 index 000000000..43f8c9a46 --- /dev/null +++ b/test/server/functional/subscription.spec.coffee @@ -0,0 +1,1316 @@ +async = require 'async' +config = require '../../../server_config' +require '../common' +utils = require '../../../app/core/utils' # Must come after require /common +mongoose = require 'mongoose' +TRAVIS = process.env.COCO_TRAVIS_TEST + +# sample data that comes in through the webhook when you subscribe +invoiceChargeSampleEvent = { + id: 'evt_155TBeKaReE7xLUdrKM72O5R', + created: 1417574898, + livemode: false, + type: 'invoice.payment_succeeded', + data: { + object: { + date: 1417574897, + id: 'in_155TBdKaReE7xLUdv8z8ipWl', + period_start: 1417574897, + period_end: 1417574897, + lines: {}, + subtotal: 999, + total: 999, + customer: 'cus_5Fz9MVWP2bDPGV', + object: 'invoice', + attempted: true, + closed: true, + forgiven: false, + paid: true, + livemode: false, + attempt_count: 1, + amount_due: 999, + currency: 'usd', + starting_balance: 0, + ending_balance: 0, + next_payment_attempt: null, + webhooks_delivered_at: null, + charge: 'ch_155TBdKaReE7xLUdRU0WcMzR', + discount: null, + application_fee: null, + subscription: 'sub_5Fz99gXrBtreNe', + metadata: {}, + statement_description: null, + description: null, + receipt_number: null + } + }, + object: 'event', + pending_webhooks: 1, + request: 'iar_5Fz9c4BZJyNNsM', + api_version: '2015-02-18' +} + +customerSubscriptionDeletedSampleEvent = { + id: 'evt_155Tj4KaReE7xLUdpoMx0UaA', + created: 1417576970, + livemode: false, + type: 'customer.subscription.deleted', + data: { + object: { + id: 'sub_5FziOkege03vT7', + plan: [Object], + object: 'subscription', + start: 1417576967, + status: 'canceled', + customer: 'cus_5Fzi54gMvGG9Px', + cancel_at_period_end: true, + current_period_start: 1417576967, + current_period_end: 1420255367, + ended_at: 1417576970, + trial_start: null, + trial_end: null, + canceled_at: 1417576970, + quantity: 1, + application_fee_percent: null, + discount: null, + metadata: {} + } + }, + object: 'event', + pending_webhooks: 1, + request: 'iar_5FziYQJ4oQdL6w', + api_version: '2015-02-18' +} + + +describe '/db/user, editing stripe property', -> + + stripe = require('stripe')(config.stripe.secretKey) + userURL = getURL('/db/user') + webhookURL = getURL('/stripe/webhook') + headers = {'X-Change-Plan': 'true'} + + it 'clears the db first', (done) -> + clearModels [User, Payment], (err) -> + throw err if err + done() + + it 'denies anonymous users trying to subscribe', (done) -> + request.get getURL('/auth/whoami'), (err, res, body) -> + body = JSON.parse(body) + body.stripe = { planID: 'basic', token: '12345' } + request.put {uri: userURL, json: body, headers: headers}, (err, res, body) -> + expect(res.statusCode).toBe 403 + done() + + #- shared data between tests + joeData = null + firstSubscriptionID = null + + it 'returns client error when a token fails to charge', (done) -> + stripe.tokens.create { + card: { number: '4000000000000002', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + stripeTokenID = token.id + loginJoe (joe) -> + joeData = joe.toObject() + joeData.stripe = { + token: stripeTokenID + planID: 'basic' + } + request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) -> + expect(res.statusCode).toBe(402) + done() + + it 'creates a subscription when you put a token and plan', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + stripeTokenID = token.id + loginJoe (joe) -> + joeData = joe.toObject() + joeData.stripe = { + token: stripeTokenID + planID: 'basic' + } + request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) -> + joeData = body + expect(res.statusCode).toBe(200) + expect(joeData.purchased.gems).toBe(3500) + expect(joeData.stripe.customerID).toBeDefined() + expect(firstSubscriptionID = joeData.stripe.subscriptionID).toBeDefined() + expect(joeData.stripe.planID).toBe('basic') + expect(joeData.stripe.token).toBeUndefined() + done() + + it 'records a payment through the webhook', (done) -> + # Don't even want to think about hooking in tests to webhooks, so... put in some data manually + stripe.invoices.list {customer: joeData.stripe.customerID}, (err, invoices) -> + expect(invoices.data.length).toBe(1) + event = _.cloneDeep(invoiceChargeSampleEvent) + event.data.object = invoices.data[0] + + request.post {uri: webhookURL, json: event}, (err, res, body) -> + expect(res.statusCode).toBe(201) + Payment.find {}, (err, payments) -> + expect(payments.length).toBe(1) + User.findById joeData._id, (err, user) -> + expect(user.get('purchased').gems).toBe(3500) + done() + + it 'schedules the stripe subscription to be cancelled when stripe.planID is removed from the user', (done) -> + delete joeData.stripe.planID + request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) -> + joeData = body + expect(res.statusCode).toBe(200) + expect(joeData.stripe.subscriptionID).toBeDefined() + expect(joeData.stripe.planID).toBeUndefined() + expect(joeData.stripe.customerID).toBeDefined() + stripe.customers.retrieve joeData.stripe.customerID, (err, customer) -> + expect(customer.subscriptions.data.length).toBe(1) + expect(customer.subscriptions.data[0].cancel_at_period_end).toBe(true) + done() + + it 'allows you to sign up again using the same customer ID as before, no token necessary', (done) -> + joeData.stripe.planID = 'basic' + request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) -> + joeData = body + + expect(res.statusCode).toBe(200) + expect(joeData.stripe.customerID).toBeDefined() + expect(joeData.stripe.subscriptionID).toBeDefined() + expect(joeData.stripe.subscriptionID).not.toBe(firstSubscriptionID) + expect(joeData.stripe.planID).toBe('basic') + done() + + it 'will not have immediately created new payments when signing back up from a cancelled subscription', (done) -> + stripe.invoices.list {customer: joeData.stripe.customerID}, (err, invoices) -> + expect(invoices.data.length).toBe(2) + expect(invoices.data[0].total).toBe(0) + event = _.cloneDeep(invoiceChargeSampleEvent) + event.data.object = invoices.data[0] + + request.post {uri: webhookURL, json: event}, (err, res, body) -> + expect(res.statusCode).toBe(200) + Payment.find {}, (err, payments) -> + expect(payments.length).toBe(1) + User.findById joeData._id, (err, user) -> + expect(user.get('purchased').gems).toBe(3500) + done() + + it 'deletes the subscription from the user object when an event about it comes through the webhook', (done) -> + stripe.customers.retrieveSubscription joeData.stripe.customerID, joeData.stripe.subscriptionID, (err, subscription) -> + event = _.cloneDeep(customerSubscriptionDeletedSampleEvent) + event.data.object = subscription + request.post {uri: webhookURL, json: event}, (err, res, body) -> + User.findById joeData._id, (err, user) -> + expect(user.get('purchased').gems).toBe(3500) + expect(user.get('stripe').subscriptionID).toBeUndefined() + expect(user.get('stripe').planID).toBeUndefined() + done() + + it "updates the customer's email when you change the user's email", (done) -> + joeData.email = 'newEmail@gmail.com' + request.put {uri: userURL, json: joeData, headers: headers }, (err, res, body) -> + f = -> stripe.customers.retrieve joeData.stripe.customerID, (err, customer) -> + expect(customer.email).toBe('newEmail@gmail.com') + done() + setTimeout(f, 500) # bit of a race condition here, response returns before stripe has been updated + + +describe 'Subscriptions', -> + # TODO: Test recurring billing via webhooks + # TODO: Test error rollbacks, Stripe is authority + + stripe = require('stripe')(config.stripe.secretKey) + userURL = getURL('/db/user') + webhookURL = getURL('/stripe/webhook') + headers = {'X-Change-Plan': 'true'} + subPrice = 999 + subGems = 3500 + invoicesWebHooked = {} + + # Start helpers + + getSubscribedQuantity = (numSponsored) -> + return 0 if numSponsored < 1 + if numSponsored <= 10 + Math.round(numSponsored * subPrice * 0.8) + else + Math.round(10 * subPrice * 0.8 + (numSponsored - 10) * subPrice * 0.6) + + getUnsubscribedQuantity = (numSponsored) -> + return 0 if numSponsored < 1 + if numSponsored <= 1 + subPrice + else if numSponsored <= 11 + Math.round(subPrice + (numSponsored - 1) * subPrice * 0.8) + else + Math.round(subPrice + 10 * subPrice * 0.8 + (numSponsored - 11) * subPrice * 0.6) + + verifyNotRecipient = (userID, done) -> + User.findById userID, (err, user) -> + expect(err).toBeNull() + if stripeInfo = user.get('stripe') + expect(stripeInfo.sponsorID).toBeUndefined() + done() + + verifyNotSponsoring = (sponsorID, recipientID, done) -> + # console.log 'verifyNotSponsoring', sponsorID, recipientID + User.findById sponsorID, (err, sponsor) -> + expect(err).toBeNull() + expect(sponsor).not.toBeNull() + return done() unless sponsor + stripeInfo = sponsor.get('stripe') + return done() unless stripeInfo?.customerID? + checkSubscriptions = (starting_after, done) -> + options = {} + options.starting_after = starting_after if starting_after + stripe.customers.listSubscriptions stripeInfo.customerID, options, (err, subscriptions) -> + expect(err).toBeNull() + for subscription in subscriptions.data + if subscription.plan.id is 'basic' + expect(subscription.metadata.id).not.toEqual(recipientID) + if subscription.plan.id is 'incremental' + expect(subscription.metadata.id).toEqual(sponsorID) + if subscriptions.has_more + checkSubscriptions subscriptions.data[subscriptions.data.length - 1].id, done + else + done() + checkSubscriptions null, done + + verifySponsorship = (sponsorUserID, sponsoredUserID, done) -> + # console.log 'verifySponsorship', sponsorUserID, sponsoredUserID + User.findById sponsorUserID, (err, user) -> + expect(err).toBeNull() + expect(user).not.toBeNull() + return done() unless user + sponsorStripe = user.get('stripe') + sponsorCustomerID = sponsorStripe.customerID + numSponsored = sponsorStripe.recipients?.length + expect(sponsorCustomerID).toBeDefined() + expect(sponsorStripe.sponsorSubscriptionID).toBeDefined() + expect(sponsorStripe.token).toBeUndefined() + expect(numSponsored).toBeGreaterThan(0) + + # Verify Stripe sponsor subscription data + return done() unless sponsorCustomerID and sponsorStripe.sponsorSubscriptionID + stripe.customers.retrieveSubscription sponsorCustomerID, sponsorStripe.sponsorSubscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription.plan.amount).toEqual(1) + expect(subscription.customer).toEqual(sponsorCustomerID) + expect(subscription.quantity).toEqual(utils.getSponsoredSubsAmount(subPrice, numSponsored, sponsorStripe.subscriptionID?)) + + # Verify sponsor payment + # May be greater than expected amount due to multiple subscribes and unsubscribes + paymentQuery = + purchaser: mongoose.Types.ObjectId(sponsorUserID) + recipient: mongoose.Types.ObjectId(sponsorUserID) + "stripe.customerID": sponsorCustomerID + "stripe.subscriptionID": sponsorStripe.sponsorSubscriptionID + expectedAmount = utils.getSponsoredSubsAmount(subPrice, numSponsored, sponsorStripe.subscriptionID?) + Payment.find paymentQuery, (err, payments) -> + expect(err).toBeNull() + expect(payments).not.toBeNull() + amount = 0 + for payment in payments + amount += payment.get('amount') + expect(payment.get('gems')).toBeUndefined() + + # NOTE: this amount may be greater than the expected amount due to proration accumlation + # NOTE: during localy execution, this is usually only 1-2 cents + expect(amount).toBeGreaterThan(expectedAmount - 50) + + # Find recipient info from sponsor stripe data + for r in sponsorStripe.recipients + if r.userID is sponsoredUserID + recipientInfo = r + break + expect(recipientInfo).toBeDefined() + expect(recipientInfo.subscriptionID).toBeDefined() + expect(recipientInfo.subscriptionID).toNotEqual(sponsorStripe.sponsorSubscriptionID) + expect(recipientInfo.couponID).toEqual('free') + + # Verify Stripe recipient subscription data + return done() unless sponsorCustomerID and recipientInfo.subscriptionID + stripe.customers.retrieveSubscription sponsorCustomerID, recipientInfo.subscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription.plan.amount).toEqual(subPrice) + expect(subscription.customer).toEqual(sponsorCustomerID) + expect(subscription.quantity).toEqual(1) + expect(subscription.metadata.id).toEqual(sponsoredUserID) + expect(subscription.discount.coupon.id).toEqual(recipientInfo.couponID) + + # Verify recipient internal data + User.findById sponsoredUserID, (err, recipient) -> + expect(err).toBeNull() + stripeInfo = recipient.get('stripe') + expect(stripeInfo.sponsorID).toEqual(sponsorUserID) + unless stripeInfo.sponsorSubscriptionID? + expect(stripeInfo.customerID).toBeUndefined() + expect(stripeInfo.token).toBeUndefined() + expect(recipient.get('purchased').gems).toBeGreaterThan(subGems - 1) + expect(recipient.isPremium()).toEqual(true) + + # Verify recipient payment + # TODO: Not accurate enough when resubscribing a user + paymentQuery = + purchaser: mongoose.Types.ObjectId(sponsorUserID) + recipient: mongoose.Types.ObjectId(sponsoredUserID) + "stripe.customerID": sponsorCustomerID + Payment.findOne paymentQuery, (err, payment) -> + expect(err).toBeNull() + expect(payment).not.toBeNull() + expect(payment.get('amount')).toEqual(0) + expect(payment.get('gems')).toBeGreaterThan(subGems - 1) + done() + + subscribeUser = (user, token, prepaidCode, done) -> + requestBody = user.toObject() + requestBody.stripe = + planID: 'basic' + requestBody.stripe.token = token.id if token? + requestBody.stripe.prepaidCode = prepaidCode if prepaidCode? + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + return done() if err + expect(res.statusCode).toBe(200) + expect(body.stripe.customerID).toBeDefined() + expect(body.stripe.planID).toBe('basic') + expect(body.stripe.token).toBeUndefined() + if prepaidCode? + expect(body.stripe.prepaidCode).toEqual(prepaidCode) + expect(body.stripe.couponID).toEqual('free') + expect(body.purchased.gems).toBeGreaterThan(subGems - 1) + User.findById user.id, (err, user) -> + stripeInfo = user.get('stripe') + expect(stripeInfo.customerID).toBeDefined() + expect(stripeInfo.planID).toBe('basic') + expect(stripeInfo.token).toBeUndefined() + if prepaidCode? + expect(stripeInfo.prepaidCode).toEqual(prepaidCode) + expect(stripeInfo.couponID).toEqual('free') + expect(user.get('purchased').gems).toBeGreaterThan(subGems - 1) + done() + + unsubscribeUser = (user, done) -> + requestBody = user.toObject() + delete requestBody.stripe.planID + delete requestBody.stripe.prepaidCode + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + User.findById user.id, (err, user) -> + expect(user.get('stripe').customerID).toBeDefined() + expect(user.get('stripe').planID).toBeUndefined() + expect(user.get('stripe').token).toBeUndefined() + stripe.customers.retrieveSubscription user.get('stripe').customerID, user.get('stripe').subscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + expect(subscription?.cancel_at_period_end).toEqual(true) + done() + + subscribeRecipients = (sponsor, recipients, token, done) -> + # console.log 'subscribeRecipients', sponsor.id, (recipient.id for recipient in recipients), token? + requestBody = sponsor.toObject() + requestBody.stripe = + subscribeEmails: (recipient.get('email') for recipient in recipients) + requestBody.stripe.token = token.id if token? + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + return done() if err + expect(res.statusCode).toBe(200) + expect(body.stripe.customerID).toBeDefined() + updatedUser = body + + # Call webhooks for invoices + options = customer: body.stripe.customerID, limit: 100 + stripe.invoices.list options, (err, invoices) -> + expect(err).toBeNull() + expect(invoices).not.toBeNull() + return done(updatedUser) unless invoices? + expect(invoices.has_more).toEqual(false) + makeWebhookCall = (invoice) -> + (callback) -> + event = _.cloneDeep(invoiceChargeSampleEvent) + event.data.object = invoice + # console.log 'Calling webhook', event.type, invoice.id + request.post {uri: webhookURL, json: event}, (err, res, body) -> + callback err + webhookTasks = [] + for invoice in invoices.data + unless invoice.id of invoicesWebHooked + invoicesWebHooked[invoice.id] = true + webhookTasks.push makeWebhookCall(invoice) + async.parallel webhookTasks, (err, results) -> + expect(err?).toEqual(false) + done(updatedUser) + + unsubscribeRecipient = (sponsor, recipient, done) -> + # console.log 'unsubscribeRecipient', sponsor.id, recipient.id + stripeInfo = sponsor.get('stripe') + customerID = stripeInfo.customerID + expect(stripeInfo.recipients).toBeDefined() + return done() unless stripeInfo.recipients + for r in stripeInfo.recipients + if r.userID is recipient.id + subscriptionID = r.subscriptionID + break + expect(customerID).toBeDefined() + expect(subscriptionID).toBeDefined() + + # Find Stripe subscription + stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + + # Call unsubscribe API + requestBody = sponsor.toObject() + requestBody.stripe = unsubscribeEmail: recipient.get('email') + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + done() + + # Subscribe a bunch of recipients at once, used for bulk discount testing + class SubbedRecipients + constructor: (@count, @toVerify) -> + @index = 0 + @recipients = [] + + length: -> + @recipients.length + + get: (i) -> + @recipients[i] + + createRecipients: (done) -> + return done() if @recipients.length is @count + createNewUser (user) => + @recipients.push user + @createRecipients done + + subRecipients: (user1, token=null, done) -> + # console.log 'subRecipients', user1.id, @recipients.length + User.findById user1.id, (err, user1) => + subscribeRecipients user1, @recipients, token, (updatedUser) => + verifyIndex = 0 + verify = => + return done(updatedUser) if verifyIndex >= @toVerify.length + verifySponsorship user1.id, @recipients[verifyIndex].id, => + verifyIndex++ + verify() + verify() + + # End helpers + + + # TODO: Use beforeAll() + it 'Clear database users and payments', (done) -> + clearModels [User, Payment], (err) -> + throw err if err + done() + + describe 'Personal', -> + it 'Subscribe user with new token', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + loginNewUser (user1) -> + subscribeUser user1, token, null, done + + it 'User delete unsubscribes', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + loginNewUser (user1) -> + subscribeUser user1, token, null, -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + customerID = user1.get('stripe').customerID + subscriptionID = user1.get('stripe').subscriptionID + request.del {uri: "#{userURL}/#{user1.id}"}, (err, res) -> + expect(err).toBeNull() + stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription?.cancel_at_period_end).toEqual(true) + done() + + it 'Admin subscribes self with valid prepaid', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + createPrepaid 'subscription', (err, res, prepaid) -> + expect(err).toBeNull() + subscribeUser user1, null, prepaid.code, -> + Prepaid.findById prepaid._id, (err, prepaid) -> + expect(err).toBeNull() + expect(prepaid.get('status')).toEqual('used') + done() + + it 'User subscribes, deletes themselves, subscription ends', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + loginNewUser (user1) -> + # Subscribe user + subscribeUser user1, token, null, -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + customerID = user1.get('stripe').customerID + subscriptionID = user1.get('stripe').subscriptionID + stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + # Delete user + request.del {uri: "#{userURL}/#{user1.id}"}, (err, res) -> + expect(err).toBeNull() + # Simulate Stripe subscription deleted via webhook + event = _.cloneDeep(customerSubscriptionDeletedSampleEvent) + event.data.object = subscription + request.post {uri: webhookURL, json: event}, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toEqual(200) + done() + + it 'Admin subscribes self with invalid prepaid', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + requestBody = user1.toObject() + requestBody.stripe = + planID: 'basic' + requestBody.stripe.prepaidCode = 'MattMatt' + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(404) + done() + + it 'User2 subscribes with used prepaid', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + createPrepaid 'subscription', (err, res, prepaid) -> + expect(err).toBeNull() + subscribeUser user1, null, prepaid.code, -> + loginNewUser (user2) -> + requestBody = user2.toObject() + requestBody.stripe = + planID: 'basic' + requestBody.stripe.prepaidCode = prepaid.code + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Subscribe normally, subscribe with valid prepaid', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + subscribeUser user1, token, null, -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + createPrepaid 'subscription', (err, res, prepaid) -> + expect(err).toBeNull() + subscribeUser user1, null, prepaid.code, -> + Prepaid.findById prepaid._id, (err, prepaid) -> + expect(err).toBeNull() + expect(prepaid.get('status')).toEqual('used') + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + customerID = user1.get('stripe').customerID + subscriptionID = user1.get('stripe').subscriptionID + stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + expect(subscription.discount?.coupon?.id).toEqual('free') + done() + + it 'Subscribe with coupon, subscribe with valid prepaid', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + requestBody = user1.toObject() + requestBody.stripe = + planID: 'basic' + token: token.id + couponID: '20pct' + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, updatedUser) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + createPrepaid 'subscription', (err, res, prepaid) -> + subscribeUser user1, null, prepaid.code, -> + Prepaid.findById prepaid._id, (err, prepaid) -> + expect(err).toBeNull() + expect(prepaid.get('status')).toEqual('used') + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + customerID = user1.get('stripe').customerID + subscriptionID = user1.get('stripe').subscriptionID + stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + expect(subscription.discount?.coupon?.id).toEqual('free') + done() + + it 'Subscribe with prepaid, then cancel', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + createPrepaid 'subscription', (err, res, prepaid) -> + expect(err).toBeNull() + subscribeUser user1, null, prepaid.code, -> + Prepaid.findById prepaid._id, (err, prepaid) -> + expect(err).toBeNull() + expect(prepaid.get('status')).toEqual('used') + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + unsubscribeUser user1, -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + expect(user1.get('stripe').prepaidCode).toEqual(prepaid.get('code')) + done() + + it 'Subscribe with prepaid, then delete', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + createPrepaid 'subscription', (err, res, prepaid) -> + expect(err).toBeNull() + subscribeUser user1, null, prepaid.code, -> + Prepaid.findById prepaid._id, (err, prepaid) -> + expect(err).toBeNull() + expect(prepaid.get('status')).toEqual('used') + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + unsubscribeUser user1, -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + stripeInfo = user1.get('stripe') + expect(stripeInfo.prepaidCode).toEqual(prepaid.get('code')) + + # Delete subscription + stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.subscriptionID, (err, subscription) -> + expect(err).toBeNull() + event = _.cloneDeep(customerSubscriptionDeletedSampleEvent) + event.data.object = subscription + request.post {uri: webhookURL, json: event}, (err, res, body) -> + expect(err).toBeNull() + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + stripeInfo = user1.get('stripe') + expect(stripeInfo.planID).toBeUndefined() + expect(stripeInfo.prepaidCode).toBeUndefined() + expect(stripeInfo.subscriptionID).toBeUndefined() + done() + + describe 'Sponsored', -> + it 'Unsubscribed user1 subscribes user2', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + verifySponsorship user1.id, user2.id, done + + it 'Recipient user delete unsubscribes', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + customerID = updatedUser.stripe.customerID + subscriptionID = updatedUser.stripe.recipients[0].subscriptionID + loginUser user2, (user2) -> + request.del {uri: "#{userURL}/#{user2.id}"}, (err, res) -> + expect(err).toBeNull() + stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) -> + expect(err).not.toBeNull() + expect(subscription).toBeNull() + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + expect(_.isEmpty(user1.get('stripe').recipients)) + stripe.customers.retrieveSubscription customerID, user1.get('stripe').sponsorSubscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription.quantity).toEqual(0) + done() + + it 'Subscribed user1 subscribes user2, one token', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeUser user1, token, null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + subscribeRecipients user1, [user2], null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + expect(user1.get('stripe').subscriptionID).toBeDefined() + expect(user1.isPremium()).toEqual(true) + verifySponsorship user1.id, user2.id, done + + it 'Clean up sponsorships upon sub cancel after setup sponsor sub fails', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeUser user1, token, null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + subscribeRecipients user1, [user2], null, (updatedUser) -> + + # Delete user1 sponsorSubscriptionID to simulate failed sponsor sub + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + stripeInfo = _.cloneDeep(user1.get('stripe') ? {}) + delete stripeInfo.sponsorSubscriptionID + user1.set 'stripe', stripeInfo + user1.save (err, user1) -> + expect(err).toBeNull() + + User.findById user1.id, (err, user1) -> + unsubscribeRecipient user1, user2, -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + expect(user1.get('stripe').subscriptionID).toBeDefined() + expect(_.isEmpty(user1.get('stripe').recipients)).toEqual(true) + expect(user1.isPremium()).toEqual(true) + User.findById user2.id, (err, user2) -> + verifyNotSponsoring user1.id, user2.id, -> + verifyNotRecipient user2.id, done + + + it 'Unsubscribed user1 unsubscribes user2 and their sub ends', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + User.findById user1.id, (err, user1) -> + unsubscribeRecipient user1, user2, -> + verifyNotSponsoring user1.id, user2.id, -> + verifyNotRecipient user2.id, done + + it 'Unsubscribed user1 immediately resubscribes user2, one token', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + User.findById user1.id, (err, user1) -> + unsubscribeRecipient user1, user2, -> + subscribeRecipients user1, [user2], null, (updatedUser) -> + verifySponsorship user1.id, user2.id, done + + it 'Sponsored user2 subscribes their sponsor user1', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + loginUser user2, (user2) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + subscribeRecipients user2, [user1], token, (updatedUser) -> + verifySponsorship user1.id, user2.id, -> + verifySponsorship user2.id, user1.id, done + + it 'Unsubscribed user1 subscribes user1', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + loginNewUser (user1) -> + + requestBody = user1.toObject() + requestBody.stripe = + subscribeEmails: [user1.get('email')] + token: token.id + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + + User.findById user1.id, (err, user) -> + expect(err).toBeNull() + stripeInfo = user.get('stripe') + expect(stripeInfo.customerID).toBeDefined() + expect(stripeInfo.planID).toBeUndefined() + expect(stripeInfo.subscriptionID).toBeUndefined() + expect(stripeInfo.recipients.length).toEqual(0) + done() + + it 'Subscribed user1 unsubscribes user2', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeUser user1, token, null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + subscribeRecipients user1, [user2], null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + unsubscribeRecipient user1, user2, -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + expect(user1.get('stripe').subscriptionID).toBeDefined() + expect(user1.isPremium()).toEqual(true) + User.findById user2.id, (err, user2) -> + verifyNotSponsoring user1.id, user2.id, -> + verifyNotRecipient user2.id, done + + it 'Subscribed user1 subscribes user2, unsubscribes themselves', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeUser user1, token, null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + subscribeRecipients user1, [user2], null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + unsubscribeUser user1, -> + verifySponsorship user1.id, user2.id, done + + it 'Sponsored user2 tries to subscribe', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + loginUser user2, (user2) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + requestBody = user2.toObject() + requestBody.stripe = + token: token.id + planID: 'basic' + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Sponsored user2 tries to subscribe with valid prepaid', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + loginUser user2, (user2) -> + user2.set('permissions', ['admin']) + user2.save (err, user1) -> + expect(err).toBeNull() + expect(user2.isAdmin()).toEqual(true) + createPrepaid 'subscription', (err, res, prepaid) -> + expect(err).toBeNull() + requestBody = user2.toObject() + requestBody.stripe = + planID: 'basic' + prepaidCode: prepaid.code + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(403) + done() + + it 'Sponsored user2 tries to unsubscribe', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + loginUser user2, (user2) -> + requestBody = user2.toObject() + requestBody.stripe = + recipient: user2.id + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + verifySponsorship user1.id, user2.id, done + + it 'Cancel sponsor subscription with 2 recipient subscriptions, then subscribe 1 old and 1 new', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user3) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2, user3], token, (updatedUser) -> + customerID = updatedUser.stripe.customerID + subscriptionID = updatedUser.stripe.sponsorSubscriptionID + + # Find Stripe sponsor subscription + stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + + # Cancel Stripe sponsor subscription + stripe.customers.cancelSubscription customerID, subscriptionID, (err) -> + expect(err).toBeNull() + + # Simulate customer.subscription.deleted webhook event for sponsor subscription + event = _.cloneDeep(customerSubscriptionDeletedSampleEvent) + event.data.object = subscription + request.post {uri: webhookURL, json: event}, (err, res, body) -> + expect(err).toBeNull() + + # Should have 2 cancelled recipient subs with cancel_at_period_end = true + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + stripeInfo = user1.get('stripe') + expect(stripeInfo.sponsorSubscriptionID).toBeUndefined() + expect(stripeInfo.recipients).toBeUndefined() + stripe.customers.listSubscriptions stripeInfo.customerID, (err, subscriptions) -> + expect(err).toBeNull() + expect(subscriptions.data.length).toEqual(2) + for sub in subscriptions.data + expect(sub.plan.id).toEqual('basic') + expect(sub.cancel_at_period_end).toEqual(true) + + # Subscribe user3 back + User.findById user1.id, (err, user1) -> + subscribeRecipients user1, [user3], null, (updatedUser) -> + verifySponsorship user1.id, user3.id, -> + + # Subscribe new user4 + createNewUser (user4) -> + loginUser user1, (user1) -> + User.findById user1.id, (err, user1) -> + subscribeRecipients user1, [user4], null, (updatedUser) -> + verifySponsorship user1.id, user4.id, done + + it 'Subscribing two users separately yields proration payment', (done) -> + # TODO: Use test plan with low duration + setTimeout to test delay between 2 subscribes + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user3) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + User.findById user1.id, (err, user1) -> + subscribeRecipients user1, [user3], null, (updatedUser) -> + # TODO: What do we expect invoices to show here? + stripe.invoices.list {customer: updatedUser.stripe.customerID}, (err, invoices) -> + expect(err).toBeNull() + + # Verify for proration invoice + foundProratedInvoice = false + for invoice in invoices.data + line = invoice.lines.data[0] + if line.type is 'invoiceitem' and line.proration + totalAmount = utils.getSponsoredSubsAmount(subPrice, 2, false) + expect(invoice.total).toBeLessThan(totalAmount) + expect(invoice.total).toEqual(totalAmount - subPrice) + Payment.findOne "stripe.invoiceID": invoice.id, (err, payment) -> + expect(err).toBeNull() + expect(payment.get('amount')).toEqual(invoice.total) + done() + foundProratedInvoice = true + break + unless foundProratedInvoice + expect(foundProratedInvoice).toEqual(true) + done() + + it 'Invalid subscribeEmails', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + loginNewUser (user1) -> + requestBody = user1.toObject() + requestBody.stripe = + subscribeEmails: ['invalid@user.com', 'notemailformat', '', null, undefined] + requestBody.stripe.token = token.id + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + expect(body.stripe).toBeDefined() + User.findById user1.id, (err, sponsor) -> + expect(err).toBeNull() + expect(sponsor.get('stripe')).toBeDefined() + expect(sponsor.get('stripe').customerID).toBeDefined() + expect(sponsor.get('stripe').sponsorSubscriptionID).toBeDefined() + expect(sponsor.get('stripe').recipients?.length).toEqual(0) + done() + + it 'User1 subscribes user2 then themselves', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2], token, (updatedUser) -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + verifySponsorship user1.id, user2.id, -> + + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + subscribeUser user1, token, null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + expect(user1.get('stripe').subscriptionID).toBeDefined() + expect(user1.isPremium()).toEqual(true) + + stripe.customers.listSubscriptions user1.get('stripe').customerID, (err, subscriptions) -> + expect(err).toBeNull() + expect(subscriptions.data.length).toEqual(3) + for sub in subscriptions.data + if sub.plan.id is 'basic' + if sub.discount?.coupon?.id is 'free' + expect(sub.metadata?.id).toEqual(user2.id) + else + expect(sub.metadata?.id).toEqual(user1.id) + else + expect(sub.plan.id).toEqual('incremental') + expect(sub.metadata?.id).toEqual(user1.id) + done() + + it 'Subscribe with prepaid, then get sponsored', (done) -> + loginNewUser (user1) -> + user1.set('permissions', ['admin']) + user1.save (err, user1) -> + expect(err).toBeNull() + expect(user1.isAdmin()).toEqual(true) + createPrepaid 'subscription', (err, res, prepaid) -> + expect(err).toBeNull() + subscribeUser user1, null, prepaid.code, -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + loginNewUser (user2) -> + requestBody = user2.toObject() + requestBody.stripe = + token: token.id + subscribeEmails: [user1.get('emailLower')] + request.put {uri: userURL, json: requestBody, headers: headers }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + User.findById user1.id, (err, user) -> + expect(err).toBeNull() + stripeInfo = user.get('stripe') + expect(stripeInfo.customerID).toBeDefined() + expect(stripeInfo.planID).toBeDefined() + expect(stripeInfo.subscriptionID).toBeDefined() + expect(stripeInfo.sponsorID).toBeUndefined() + done() + + + describe 'Bulk discounts', -> + # Bulk discount algorithm (includes personal sub): + # 1 100% + # 2-11 80% + # 12+ 60% + + it 'Unsubscribed user1 subscribes two users', (done) -> + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + createNewUser (user3) -> + createNewUser (user2) -> + loginNewUser (user1) -> + subscribeRecipients user1, [user2, user3], token, (updatedUser) -> + verifySponsorship user1.id, user2.id, -> + verifySponsorship user1.id, user3.id, done + + it 'Subscribed user1 subscribes 2 users, unsubscribes 2', (done) -> + recipientCount = 2 + recipientsToVerify = [0, 1] + recipients = new SubbedRecipients recipientCount, recipientsToVerify + + # Create recipients + recipients.createRecipients -> + expect(recipients.length()).toEqual(recipientCount) + + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + + # Create sponsor user + loginNewUser (user1) -> + subscribeUser user1, token, null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + + # Subscribe recipients + recipients.subRecipients user1, null, -> + User.findById user1.id, (err, user1) -> + + # Unsubscribe recipient0 + unsubscribeRecipient user1, recipients.get(0), -> + User.findById user1.id, (err, user1) -> + stripeInfo = user1.get('stripe') + expect(stripeInfo.recipients.length).toEqual(1) + verifyNotSponsoring user1.id, recipients.get(0).id, -> + verifyNotRecipient recipients.get(0).id, -> + stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + expect(subscription.quantity).toEqual(getSubscribedQuantity(1)) + + # Unsubscribe recipient1 + unsubscribeRecipient user1, recipients.get(1), -> + User.findById user1.id, (err, user1) -> + stripeInfo = user1.get('stripe') + expect(stripeInfo.recipients.length).toEqual(0) + verifyNotSponsoring user1.id, recipients.get(1).id, -> + verifyNotRecipient recipients.get(1).id, -> + stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + expect(subscription.quantity).toEqual(0) + done() + + unless TRAVIS + it 'Subscribed user1 subscribes 3 users, unsubscribes 2, themselves, then 1', (done) -> + recipientCount = 3 + recipientsToVerify = [0, 1, 2] + recipients = new SubbedRecipients recipientCount, recipientsToVerify + + # Create recipients + recipients.createRecipients -> + expect(recipients.length()).toEqual(recipientCount) + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + + # Create sponsor user + loginNewUser (user1) -> + subscribeUser user1, token, null, (updatedUser) -> + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + + # Subscribe recipients + recipients.subRecipients user1, null, -> + User.findById user1.id, (err, user1) -> + + # Unsubscribe first recipient + unsubscribeRecipient user1, recipients.get(0), -> + User.findById user1.id, (err, user1) -> + stripeInfo = user1.get('stripe') + expect(stripeInfo.recipients.length).toEqual(recipientCount - 1) + verifyNotSponsoring user1.id, recipients.get(0).id, -> + verifyNotRecipient recipients.get(0).id, -> + stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + expect(subscription.quantity).toEqual(getSubscribedQuantity(recipientCount - 1)) + + # Unsubscribe second recipient + unsubscribeRecipient user1, recipients.get(1), -> + User.findById user1.id, (err, user1) -> + stripeInfo = user1.get('stripe') + expect(stripeInfo.recipients.length).toEqual(recipientCount - 2) + verifyNotSponsoring user1.id, recipients.get(1).id, -> + verifyNotRecipient recipients.get(1).id, -> + stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + expect(subscription.quantity).toEqual(getSubscribedQuantity(recipientCount - 2)) + + # Unsubscribe self + User.findById user1.id, (err, user1) -> + unsubscribeUser user1, -> + User.findById user1.id, (err, user1) -> + stripeInfo = user1.get('stripe') + expect(stripeInfo.planID).toBeUndefined() + + # Unsubscribe third recipient + verifySponsorship user1.id, recipients.get(2).id, -> + unsubscribeRecipient user1, recipients.get(2), -> + User.findById user1.id, (err, user1) -> + stripeInfo = user1.get('stripe') + expect(stripeInfo.recipients.length).toEqual(recipientCount - 3) + verifyNotSponsoring user1.id, recipients.get(2).id, -> + verifyNotRecipient recipients.get(2).id, -> + stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + expect(subscription.quantity).toEqual(getSubscribedQuantity(recipientCount - 3)) + done() + + xit 'Unsubscribed user1 subscribes 13 users, unsubcribes 2', (done) -> + # TODO: Hits the Stripe error 'Request rate limit exceeded'. + # TODO: Need a better test for 12+ bulk discounts. Or, we could update the bulk disount logic. + # TODO: verify interim invoices? + recipientCount = 13 + recipientsToVerify = [0, 1, 10, 11, 12] + recipients = new SubbedRecipients recipientCount, recipientsToVerify + + # Create recipients + recipients.createRecipients -> + expect(recipients.length()).toEqual(recipientCount) + + stripe.tokens.create { + card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } + }, (err, token) -> + + # Create sponsor user + loginNewUser (user1) -> + + # Subscribe recipients + recipients.subRecipients user1, token, -> + User.findById user1.id, (err, user1) -> + + # Unsubscribe first recipient + unsubscribeRecipient user1, recipients.get(0), -> + User.findById user1.id, (err, user1) -> + + stripeInfo = user1.get('stripe') + expect(stripeInfo.recipients.length).toEqual(recipientCount - 1) + verifyNotSponsoring user1.id, recipients.get(0).id, -> + verifyNotRecipient recipients.get(0).id, -> + stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + expect(subscription.quantity).toEqual(getUnsubscribedQuantity(recipientCount - 1)) + + # Unsubscribe last recipient + unsubscribeRecipient user1, recipients.get(recipientCount - 1), -> + User.findById user1.id, (err, user1) -> + stripeInfo = user1.get('stripe') + expect(stripeInfo.recipients.length).toEqual(recipientCount - 2) + verifyNotSponsoring user1.id, recipients.get(recipientCount - 1).id, -> + verifyNotRecipient recipients.get(recipientCount - 1).id, -> + stripe.customers.retrieveSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, (err, subscription) -> + expect(err).toBeNull() + expect(subscription).not.toBeNull() + numSponsored = recipientCount - 2 + if numSponsored <= 1 + expect(subscription.quantity).toEqual(subPrice) + else if numSponsored <= 11 + expect(subscription.quantity).toEqual(subPrice + (numSponsored - 1) * subPrice * 0.8) + else + expect(subscription.quantity).toEqual(subPrice + 10 * subPrice * 0.8 + (numSponsored - 11) * subPrice * 0.6) + done() diff --git a/test/server/functional/trial_request.spec.coffee b/test/server/functional/trial_request.spec.coffee new file mode 100644 index 000000000..3069a25d5 --- /dev/null +++ b/test/server/functional/trial_request.spec.coffee @@ -0,0 +1,162 @@ +require '../common' + +describe 'Trial Requests', -> + URL = getURL('/db/trial.request') + ownURL = getURL('/db/trial.request/-/own') + + createTrialRequest = (user, type, properties, done) -> + requestBody = + type: type + properties: properties + request.post {uri: URL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + expect(body.type).toEqual(type) + expect(body.properties).toEqual(properties) + expect(body.applicant).toEqual(user.id) + expect(body.status).toEqual('submitted') + TrialRequest.findById body._id, (err, doc) -> + expect(err).toBeNull() + expect(doc.get('type')).toEqual(type) + expect(doc.get('properties')).toEqual(properties) + expect(doc.get('applicant')).toEqual(user._id) + expect(doc.get('status')).toEqual('submitted') + done(doc) + + it 'Clear database', (done) -> + clearModels [User, TrialRequest], (err) -> + throw err if err + done() + + it 'Create trial request', (done) -> + loginNewUser (user) -> + properties = + email: user.get('email') + location: 'SF, CA' + age: '14-17' + numStudents: 14 + heardAbout: 'magical interwebs' + createTrialRequest user, 'subscription', properties, (trialRequest) -> + done() + + it 'Get trial requests, non-admin', (done) -> + loginNewUser (user) -> + properties = + email: user.get('email') + location: 'SF, CA' + age: '14-17' + numStudents: 14 + heardAbout: 'magical interwebs' + createTrialRequest user, 'subscription', properties, (trialRequest) -> + request.get URL, (err, res, body) -> + expect(res.statusCode).toEqual(403) + done() + + it 'Get trial requests, admin', (done) -> + loginNewUser (user) -> + properties = + email: user.get('email') + location: 'SF, CA' + age: '14-17' + numStudents: 14 + heardAbout: 'magical interwebs' + createTrialRequest user, 'subscription', properties, (trialRequest) -> + loginNewUser (admin) -> + admin.set('permissions', ['admin']) + admin.save (err, user) -> + request.get URL, (err, res, body) -> + expect(res.statusCode).toEqual(200) + expect(body.length).toBeGreaterThan(0) + done() + + it 'Get own trial requests', (done) -> + loginNewUser (user) -> + properties = + email: user.get('email') + location: 'SF, CA' + age: '14-17' + numStudents: 14 + heardAbout: 'magical interwebs' + createTrialRequest user, 'subscription', properties, (trialRequest) -> + request.get ownURL, (err, res, body) -> + expect(res.statusCode).toEqual(200) + ownRequests = JSON.parse(body) + expect(ownRequests.length).toEqual(1) + expect(ownRequests[0]._id).toEqual(trialRequest.id) + done() + + it 'Get non-owned trial request, non-admin', (done) -> + loginNewUser (user) -> + properties = + email: user.get('email') + location: 'SF, CA' + age: '14-17' + numStudents: 14 + heardAbout: 'magical interwebs' + createTrialRequest user, 'subscription', properties, (trialRequest) -> + loginNewUser (user2) -> + request.get URL + "/#{trialRequest.id}", (err, res, body) -> + expect(res.statusCode).toEqual(403) + done() + + it 'Approve trial request', (done) -> + loginNewUser (user) -> + properties = + email: user.get('email') + location: 'SF, CA' + age: '14-17' + numStudents: 14 + heardAbout: 'magical interwebs' + createTrialRequest user, 'subscription', properties, (trialRequest) -> + loginNewUser (admin) -> + admin.set('permissions', ['admin']) + admin.save (err, user) -> + requestBody = trialRequest.toObject() + requestBody.status = 'approved' + request.put {uri: URL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + expect(body.status).toEqual('approved') + expect(body.reviewDate).toBeDefined() + expect(new Date(body.reviewDate)).toBeLessThan(new Date()) + expect(body.reviewer).toEqual(admin.id) + expect(body.prepaidCode).toBeDefined() + TrialRequest.findById body._id, (err, doc) -> + expect(err).toBeNull() + expect(doc.get('status')).toEqual('approved') + expect(doc.get('reviewDate')).toBeDefined() + expect(new Date(doc.get('reviewDate'))).toBeLessThan(new Date()) + expect(doc.get('reviewer')).toEqual(admin._id) + expect(doc.get('prepaidCode')).toBeDefined() + done() + + it 'Deny trial request', (done) -> + loginNewUser (user) -> + properties = + email: user.get('email') + location: 'SF, CA' + age: '14-17' + numStudents: 14 + heardAbout: 'magical interwebs' + createTrialRequest user, 'subscription', properties, (trialRequest) -> + loginNewUser (admin) -> + admin.set('permissions', ['admin']) + admin.save (err, user) -> + requestBody = trialRequest.toObject() + requestBody.status = 'denied' + request.put {uri: URL, json: requestBody }, (err, res, body) -> + expect(err).toBeNull() + expect(res.statusCode).toBe(200) + expect(body.status).toEqual('denied') + expect(body.reviewDate).toBeDefined() + expect(new Date(body.reviewDate)).toBeLessThan(new Date()) + expect(body.reviewer).toEqual(admin.id) + expect(body.prepaidCode).not.toBeDefined() + TrialRequest.findById body._id, (err, doc) -> + expect(err).toBeNull() + expect(doc.get('status')).toEqual('denied') + expect(doc.get('reviewDate')).toBeDefined() + expect(new Date(doc.get('reviewDate'))).toBeLessThan(new Date()) + expect(doc.get('reviewer')).toEqual(admin._id) + expect(doc.get('prepaidCode')).not.toBeDefined() + done() diff --git a/test/server/functional/user.spec.coffee b/test/server/functional/user.spec.coffee index 7335d47e7..4c6b33383 100644 --- a/test/server/functional/user.spec.coffee +++ b/test/server/functional/user.spec.coffee @@ -28,7 +28,7 @@ describe 'Server user object', -> expect(JSON.stringify(user.get('emailSubscriptions'))).toBe(JSON.stringify(['tester', 'level_creator'])) done() -describe 'User.updateMailChimp', -> +describe 'User.updateServiceSettings', -> makeMC = (callback) -> GLOBAL.mc = lists: @@ -40,7 +40,7 @@ describe 'User.updateMailChimp', -> done() user = new User({emailSubscriptions: ['announcement'], email: 'tester@gmail.com'}) - User.updateMailChimp(user) + User.updateServiceSettings(user) describe 'POST /db/user', -> @@ -69,7 +69,7 @@ describe 'POST /db/user', -> expect(user.get('password')).toBeUndefined() expect(user?.get('passwordHash')).not.toBeUndefined() if user?.get('passwordHash')? - expect(user.get('passwordHash')[..5]).toBe('948c7e') + expect(user.get('passwordHash')[..5] in ['31dc3d', '948c7e']).toBeTruthy() expect(user.get('permissions').length).toBe(0) done() @@ -328,6 +328,24 @@ describe 'GET /db/user', -> xit 'can fetch another user with restricted fields' +describe 'DELETE /db/user', -> + it 'can delete a user', (done) -> + loginNewUser (user1) -> + beforeDeleted = new Date() + request.del {uri: "#{getURL(urlUser)}/#{user1.id}"}, (err, res) -> + expect(err).toBeNull() + return done() if err + User.findById user1.id, (err, user1) -> + expect(err).toBeNull() + return done() if err + expect(user1.get('deleted')).toBe(true) + expect(user1.get('dateDeleted')).toBeGreaterThan(beforeDeleted) + expect(user1.get('dateDeleted')).toBeLessThan(new Date()) + for key, value of user1.toObject() + continue if key in ['_id', 'deleted', 'dateDeleted'] + expect(_.isEmpty(value)).toEqual(true) + done() + describe 'Statistics', -> LevelSession = require '../../../server/levels/sessions/LevelSession' Article = require '../../../server/articles/Article' @@ -342,7 +360,7 @@ describe 'Statistics', -> session = new LevelSession name: 'Beat Gandalf' permissions: simplePermissions - state: completed: true + state: complete: true unittest.getNormalJoe (joe) -> expect(joe.get 'stats.gamesCompleted').toBeUndefined() diff --git a/test/server/integration/models/plugins.spec.coffee b/test/server/integration/models/plugins.spec.coffee index e7921fba7..df4882a59 100644 --- a/test/server/integration/models/plugins.spec.coffee +++ b/test/server/integration/models/plugins.spec.coffee @@ -280,10 +280,10 @@ describe 'SearchablePlugin', -> firstArticle.save (err) -> throw err if err - Article.textSearch 'best', {filter: {index: true}}, (err, results) -> + Article.find {$text: {$search: 'best'}, index: true}, (err, results) -> expect(err).toBeNull() if results - expect(results.results.length).toBeGreaterThan(0) + expect(results.length).toBeGreaterThan(0) else console.log('ERROR:', err) done() diff --git a/test/server/unit/analytics.spec.coffee b/test/server/unit/analytics.spec.coffee new file mode 100644 index 000000000..c25cad7c4 --- /dev/null +++ b/test/server/unit/analytics.spec.coffee @@ -0,0 +1,77 @@ +GLOBAL._ = require 'lodash' + +require '../common' +request = require 'request' +AnalyticsUsersActive = require '../../../server/analytics/AnalyticsUsersActive' +LevelSession = require '../../../server/levels/sessions/LevelSession' +User = require '../../../server/users/User' + +# TODO: these tests have some rerun/cleanup issues +# TODO: add tests for purchase, payment, subscribe, unsubscribe, and earned achievements + +# TODO: AnalyticsUsersActive collection isn't currently used. +# TODO: Will remove these tests if we end up ripping out the disabled saveActiveUser calls. + +describe 'Analytics', -> + + xit 'registered user', (done) -> + clearModels [AnalyticsUsersActive], (err) -> + expect(err).toBeNull() + user = new User + permissions: [] + name: "Fred" + Math.floor(Math.random() * 10000) + user.save (err) -> + expect(err).toBeNull() + userID = mongoose.Types.ObjectId(user.get('_id')) + AnalyticsUsersActive.find {creator : userID}, (err, activeUsers) -> + expect(activeUsers.length).toEqual(0) + user.register -> + AnalyticsUsersActive.find {creator : userID}, (err, activeUsers) -> + expect(err).toBeNull() + expect(activeUsers.length).toEqual(1) + expect(activeUsers[0]?.get('event')).toEqual('register') + done() + + xit 'level completed', (done) -> + clearModels [AnalyticsUsersActive], (err) -> + expect(err).toBeNull() + unittest.getNormalJoe (joe) -> + userID = mongoose.Types.ObjectId(joe.get('_id')) + session = new LevelSession + name: 'Beat Gandalf' + levelID: 'lotr' + permissions: simplePermissions + state: complete: false + creator: userID + session.save (err) -> + expect(err).toBeNull() + AnalyticsUsersActive.find {creator : userID}, (err, activeUsers) -> + expect(activeUsers.length).toEqual(0) + session.set 'state', complete: true + session.save (err) -> + expect(err).toBeNull() + AnalyticsUsersActive.find {creator : userID}, (err, activeUsers) -> + expect(err).toBeNull() + expect(activeUsers.length).toEqual(1) + expect(activeUsers[0]?.get('event')).toEqual('level-completed/lotr') + done() + + xit 'level playtime', (done) -> + clearModels [AnalyticsUsersActive], (err) -> + expect(err).toBeNull() + unittest.getNormalJoe (joe) -> + userID = mongoose.Types.ObjectId(joe.get('_id')) + session = new LevelSession + name: 'Beat Gandalf' + levelID: 'lotr' + permissions: simplePermissions + playtime: 60 + creator: userID + session.save (err) -> + expect(err).toBeNull() + AnalyticsUsersActive.find {creator : userID}, (err, activeUsers) -> + expect(err).toBeNull() + expect(activeUsers.length).toEqual(1) + expect(activeUsers[0]?.get('event')).toEqual('level-playtime/lotr') + done() + diff --git a/vendor/scripts/ShaderParticles.js b/vendor/scripts/ShaderParticles.js new file mode 100644 index 000000000..c3f49f8d4 --- /dev/null +++ b/vendor/scripts/ShaderParticles.js @@ -0,0 +1,1187 @@ +// ShaderParticleUtils 0.7.9 +// +// (c) 2014 Luke Moody (http://www.github.com/squarefeet) +// & Lee Stemkoski (http://www.adelphi.edu/~stemkoski/) +// +// Based on Lee Stemkoski's original work: +// (https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/js/ParticleEngine.js). +// +// ShaderParticleGroup may be freely distributed under the MIT license (See LICENSE.txt) + +var SPE = SPE || {}; + +SPE.utils = { + + /** + * Given a base vector and a spread range vector, create + * a new THREE.Vector3 instance with randomised values. + * + * @private + * + * @param {THREE.Vector3} base + * @param {THREE.Vector3} spread + * @return {THREE.Vector3} + */ + randomVector3: function( base, spread ) { + var v = new THREE.Vector3(); + + v.copy( base ); + + v.x += Math.random() * spread.x - (spread.x/2); + v.y += Math.random() * spread.y - (spread.y/2); + v.z += Math.random() * spread.z - (spread.z/2); + + return v; + }, + + /** + * Create a new THREE.Color instance and given a base vector and + * spread range vector, assign random values. + * + * Note that THREE.Color RGB values are in the range of 0 - 1, not 0 - 255. + * + * @private + * + * @param {THREE.Vector3} base + * @param {THREE.Vector3} spread + * @return {THREE.Color} + */ + randomColor: function( base, spread ) { + var v = new THREE.Color(); + + v.copy( base ); + + v.r += (Math.random() * spread.x) - (spread.x/2); + v.g += (Math.random() * spread.y) - (spread.y/2); + v.b += (Math.random() * spread.z) - (spread.z/2); + + v.r = Math.max( 0, Math.min( v.r, 1 ) ); + v.g = Math.max( 0, Math.min( v.g, 1 ) ); + v.b = Math.max( 0, Math.min( v.b, 1 ) ); + + return v; + }, + + /** + * Create a random Number value based on an initial value and + * a spread range + * + * @private + * + * @param {Number} base + * @param {Number} spread + * @return {Number} + */ + randomFloat: function( base, spread ) { + return base + spread * (Math.random() - 0.5); + }, + + /** + * Create a new THREE.Vector3 instance and project it onto a random point + * on a sphere with randomized radius. + * + * @param {THREE.Vector3} base + * @param {Number} radius + * @param {THREE.Vector3} radiusSpread + * @param {THREE.Vector3} radiusScale + * + * @private + * + * @return {THREE.Vector3} + */ + randomVector3OnSphere: function( base, radius, radiusSpread, radiusScale, radiusSpreadClamp ) { + var z = 2 * Math.random() - 1; + var t = 6.2832 * Math.random(); + var r = Math.sqrt( 1 - z*z ); + var vec = new THREE.Vector3( r * Math.cos(t), r * Math.sin(t), z ); + + var rand = this._randomFloat( radius, radiusSpread ); + + if( radiusSpreadClamp ) { + rand = Math.round( rand / radiusSpreadClamp ) * radiusSpreadClamp; + } + + vec.multiplyScalar( rand ); + + if( radiusScale ) { + vec.multiply( radiusScale ); + } + + vec.add( base ); + + return vec; + }, + + /** + * Create a new THREE.Vector3 instance and project it onto a random point + * on a disk (in the XY-plane) centered at `base` and with randomized radius. + * + * @param {THREE.Vector3} base + * @param {Number} radius + * @param {THREE.Vector3} radiusSpread + * @param {THREE.Vector3} radiusScale + * + * @private + * + * @return {THREE.Vector3} + */ + randomVector3OnDisk: function( base, radius, radiusSpread, radiusScale, radiusSpreadClamp ) { + var t = 6.2832 * Math.random(); + var rand = this._randomFloat( radius, radiusSpread ); + + if( radiusSpreadClamp ) { + rand = Math.round( rand / radiusSpreadClamp ) * radiusSpreadClamp; + } + + var vec = new THREE.Vector3( Math.cos(t), Math.sin(t), 0 ).multiplyScalar( rand ); + + if ( radiusScale ) { + vec.multiply( radiusScale ); + } + + vec.add( base ); + + return vec ; + }, + + + /** + * Create a new THREE.Vector3 instance, and given a sphere with center `base` and + * point `position` on sphere, set direction away from sphere center with random magnitude. + * + * @param {THREE.Vector3} base + * @param {THREE.Vector3} position + * @param {Number} speed + * @param {Number} speedSpread + * @param {THREE.Vector3} scale + * + * @private + * + * @return {THREE.Vector3} + */ + randomVelocityVector3OnSphere: function( base, position, speed, speedSpread, scale ) { + var direction = new THREE.Vector3().subVectors( base, position ); + + direction.normalize().multiplyScalar( Math.abs( this._randomFloat( speed, speedSpread ) ) ); + + if( scale ) { + direction.multiply( scale ); + } + + return direction; + }, + + + + /** + * Given a base vector and a spread vector, randomise the given vector + * accordingly. + * + * @param {THREE.Vector3} vector + * @param {THREE.Vector3} base + * @param {THREE.Vector3} spread + * + * @private + * + * @return {[type]} + */ + randomizeExistingVector3: function( v, base, spread ) { + v.copy( base ); + + v.x += Math.random() * spread.x - (spread.x/2); + v.y += Math.random() * spread.y - (spread.y/2); + v.z += Math.random() * spread.z - (spread.z/2); + }, + + + /** + * Randomize a THREE.Color instance and given a base vector and + * spread range vector, assign random values. + * + * Note that THREE.Color RGB values are in the range of 0 - 1, not 0 - 255. + * + * @private + * + * @param {THREE.Vector3} base + * @param {THREE.Vector3} spread + * @return {THREE.Color} + */ + randomizeExistingColor: function( v, base, spread ) { + v.copy( base ); + + v.r += (Math.random() * spread.x) - (spread.x/2); + v.g += (Math.random() * spread.y) - (spread.y/2); + v.b += (Math.random() * spread.z) - (spread.z/2); + + v.r = Math.max( 0, Math.min( v.r, 1 ) ); + v.g = Math.max( 0, Math.min( v.g, 1 ) ); + v.b = Math.max( 0, Math.min( v.b, 1 ) ); + }, + + /** + * Given an existing particle vector, project it onto a random point on a + * sphere with radius `radius` and position `base`. + * + * @private + * + * @param {THREE.Vector3} v + * @param {THREE.Vector3} base + * @param {Number} radius + */ + randomizeExistingVector3OnSphere: function( v, base, radius, radiusSpread, radiusScale, radiusSpreadClamp ) { + var z = 2 * Math.random() - 1, + t = 6.2832 * Math.random(), + r = Math.sqrt( 1 - z*z ), + rand = this._randomFloat( radius, radiusSpread ); + + if( radiusSpreadClamp ) { + rand = Math.round( rand / radiusSpreadClamp ) * radiusSpreadClamp; + } + + v.set( + (r * Math.cos(t)) * rand, + (r * Math.sin(t)) * rand, + z * rand + ).multiply( radiusScale ); + + v.add( base ); + }, + + + /** + * Given an existing particle vector, project it onto a random point + * on a disk (in the XY-plane) centered at `base` and with radius `radius`. + * + * @private + * + * @param {THREE.Vector3} v + * @param {THREE.Vector3} base + * @param {Number} radius + */ + randomizeExistingVector3OnDisk: function( v, base, radius, radiusSpread, radiusScale, radiusSpreadClamp ) { + var t = 6.2832 * Math.random(), + rand = Math.abs( this._randomFloat( radius, radiusSpread ) ); + + if( radiusSpreadClamp ) { + rand = Math.round( rand / radiusSpreadClamp ) * radiusSpreadClamp; + } + + v.set( + Math.cos( t ), + Math.sin( t ), + 0 + ).multiplyScalar( rand ); + + if ( radiusScale ) { + v.multiply( radiusScale ); + } + + v.add( base ); + }, + + randomizeExistingVelocityVector3OnSphere: function( v, base, position, speed, speedSpread ) { + v.copy(position) + .sub(base) + .normalize() + .multiplyScalar( Math.abs( this._randomFloat( speed, speedSpread ) ) ); + }, + + generateID: function() { + var str = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; + + str = str.replace(/[xy]/g, function(c) { + var rand = Math.random(); + var r = rand*16|0%16, v = c === 'x' ? r : (r&0x3|0x8); + + return v.toString(16); + }); + + return str; + } +};; + +// ShaderParticleGroup 0.7.9 +// +// (c) 2014 Luke Moody (http://www.github.com/squarefeet) +// & Lee Stemkoski (http://www.adelphi.edu/~stemkoski/) +// +// Based on Lee Stemkoski's original work: +// (https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/js/ParticleEngine.js). +// +// ShaderParticleGroup may be freely distributed under the MIT license (See LICENSE.txt) + +var SPE = SPE || {}; + +SPE.Group = function( options ) { + var that = this; + + that.fixedTimeStep = parseFloat( typeof options.fixedTimeStep === 'number' ? options.fixedTimeStep : 0.016 ); + + // Uniform properties ( applied to all particles ) + that.maxAge = parseFloat( options.maxAge || 3 ); + that.texture = options.texture || null; + that.hasPerspective = parseInt( typeof options.hasPerspective === 'number' ? options.hasPerspective : 1, 10 ); + that.colorize = parseInt( typeof options.colorize === 'number' ? options.colorize : 1, 10 ); + + // Material properties + that.blending = typeof options.blending === 'number' ? options.blending : THREE.AdditiveBlending; + that.transparent = typeof options.transparent === 'boolean' ? options.transparent : true; + that.alphaTest = typeof options.alphaTest === 'number' ? options.alphaTest : 0.5; + that.depthWrite = typeof options.depthWrite === 'boolean' ? options.depthWrite : false; + that.depthTest = typeof options.depthTest === 'boolean' ? options.depthTest : true; + + // Create uniforms + that.uniforms = { + duration: { type: 'f', value: that.maxAge }, + texture: { type: 't', value: that.texture }, + hasPerspective: { type: 'i', value: that.hasPerspective }, + colorize: { type: 'i', value: that.colorize } + }; + + // Create a map of attributes that will hold values for each particle in this group. + that.attributes = { + acceleration: { type: 'v3', value: [] }, + velocity: { type: 'v3', value: [] }, + + alive: { type: 'f', value: [] }, + age: { type: 'f', value: [] }, + + size: { type: 'v3', value: [] }, + angle: { type: 'v4', value: [] }, + + colorStart: { type: 'c', value: [] }, + colorMiddle: { type: 'c', value: [] }, + colorEnd: { type: 'c', value: [] }, + + opacity: { type: 'v3', value: [] } + }; + + // Emitters (that aren't static) will be added to this array for + // processing during the `tick()` function. + that.emitters = []; + + // Create properties for use by the emitter pooling functions. + that._pool = []; + that._poolCreationSettings = null; + that._createNewWhenPoolEmpty = 0; + that.maxAgeMilliseconds = that.maxAge * 1000; + + // Create an empty geometry to hold the particles. + // Each particle is a vertex pushed into this geometry's + // vertices array. + that.geometry = new THREE.Geometry(); + + // Create the shader material using the properties we set above. + that.material = new THREE.ShaderMaterial({ + uniforms: that.uniforms, + attributes: that.attributes, + vertexShader: SPE.shaders.vertex, + fragmentShader: SPE.shaders.fragment, + blending: that.blending, + transparent: that.transparent, + alphaTest: that.alphaTest, + depthWrite: that.depthWrite, + depthTest: that.depthTest + }); + + // And finally create the ParticleSystem. It's got its `dynamic` property + // set so that THREE.js knows to update it on each frame. + that.mesh = new THREE.PointCloud( that.geometry, that.material ); + that.mesh.dynamic = true; +}; + +SPE.Group.prototype = { + + /** + * Tells the age and alive attributes (and the geometry vertices) + * that they need updating by THREE.js's internal tick functions. + * + * @private + * + * @return {this} + */ + _flagUpdate: function() { + var that = this; + + // Set flags to update (causes less garbage than + // ```ParticleSystem.sortParticles = true``` in THREE.r58 at least) + that.attributes.age.needsUpdate = true; + that.attributes.alive.needsUpdate = true; + that.attributes.angle.needsUpdate = true; + // that.attributes.angleAlignVelocity.needsUpdate = true; + that.attributes.velocity.needsUpdate = true; + that.attributes.acceleration.needsUpdate = true; + that.geometry.verticesNeedUpdate = true; + + return that; + }, + + /** + * Add an emitter to this particle group. Once added, an emitter will be automatically + * updated when SPE.Group#tick() is called. + * + * @param {SPE.Emitter} emitter + * @return {this} + */ + addEmitter: function( emitter ) { + var that = this; + + if( emitter.duration ) { + emitter.particlesPerSecond = emitter.particleCount / (that.maxAge < emitter.duration ? that.maxAge : emitter.duration) | 0; + } + else { + emitter.particlesPerSecond = emitter.particleCount / that.maxAge | 0 + } + + var vertices = that.geometry.vertices, + start = vertices.length, + end = emitter.particleCount + start, + a = that.attributes, + acceleration = a.acceleration.value, + velocity = a.velocity.value, + alive = a.alive.value, + age = a.age.value, + size = a.size.value, + angle = a.angle.value, + colorStart = a.colorStart.value, + colorMiddle = a.colorMiddle.value, + colorEnd = a.colorEnd.value, + opacity = a.opacity.value; + + emitter.particleIndex = parseFloat( start ); + + // Create the values + for( var i = start; i < end; ++i ) { + + if( emitter.type === 'sphere' ) { + vertices[i] = that._randomVector3OnSphere( emitter.position, emitter.radius, emitter.radiusSpread, emitter.radiusScale, emitter.radiusSpreadClamp ); + velocity[i] = that._randomVelocityVector3OnSphere( vertices[i], emitter.position, emitter.speed, emitter.speedSpread ); + } + else if( emitter.type === 'disk' ) { + vertices[i] = that._randomVector3OnDisk( emitter.position, emitter.radius, emitter.radiusSpread, emitter.radiusScale, emitter.radiusSpreadClamp ); + velocity[i] = that._randomVelocityVector3OnSphere( vertices[i], emitter.position, emitter.speed, emitter.speedSpread ); + } + else { + vertices[i] = that._randomVector3( emitter.position, emitter.positionSpread ); + velocity[i] = that._randomVector3( emitter.velocity, emitter.velocitySpread ); + } + + acceleration[i] = that._randomVector3( emitter.acceleration, emitter.accelerationSpread ); + + size[i] = new THREE.Vector3( + Math.abs( that._randomFloat( emitter.sizeStart, emitter.sizeStartSpread ) ), + Math.abs( that._randomFloat( emitter.sizeMiddle, emitter.sizeMiddleSpread ) ), + Math.abs( that._randomFloat( emitter.sizeEnd, emitter.sizeEndSpread ) ) + ); + + angle[i] = new THREE.Vector4( + that._randomFloat( emitter.angleStart, emitter.angleStartSpread ), + that._randomFloat( emitter.angleMiddle, emitter.angleMiddleSpread ), + that._randomFloat( emitter.angleEnd, emitter.angleEndSpread ), + emitter.angleAlignVelocity ? 1.0 : 0.0 + ); + + age[i] = 0.0; + alive[i] = emitter.isStatic ? 1.0 : 0.0; + + colorStart[i] = that._randomColor( emitter.colorStart, emitter.colorStartSpread ); + colorMiddle[i] = that._randomColor( emitter.colorMiddle, emitter.colorMiddleSpread ); + colorEnd[i] = that._randomColor( emitter.colorEnd, emitter.colorEndSpread ); + + opacity[i] = new THREE.Vector3( + Math.abs( that._randomFloat( emitter.opacityStart, emitter.opacityStartSpread ) ), + Math.abs( that._randomFloat( emitter.opacityMiddle, emitter.opacityMiddleSpread ) ), + Math.abs( that._randomFloat( emitter.opacityEnd, emitter.opacityEndSpread ) ) + ); + } + + // Cache properties on the emitter so we can access + // them from its tick function. + emitter.verticesIndex = parseFloat( start ); + emitter.attributes = a; + emitter.vertices = that.geometry.vertices; + emitter.maxAge = that.maxAge; + + // Assign a unique ID to this emitter + emitter.__id = that._generateID(); + + // Save this emitter in an array for processing during this.tick() + if( !emitter.isStatic ) { + that.emitters.push( emitter ); + } + + return that; + }, + + + removeEmitter: function( emitter ) { + var id, + emitters = this.emitters; + + if( emitter instanceof SPE.Emitter ) { + id = emitter.__id; + } + else if( typeof emitter === 'string' ) { + id = emitter; + } + else { + console.warn('Invalid emitter or emitter ID passed to SPE.Group#removeEmitter.' ); + return; + } + + for( var i = 0, il = emitters.length; i < il; ++i ) { + if( emitters[i].__id === id ) { + emitters.splice(i, 1); + break; + } + } + }, + + + /** + * The main particle group update function. Call this once per frame. + * + * @param {Number} dt + * @return {this} + */ + tick: function( dt ) { + var that = this, + emitters = that.emitters, + numEmitters = emitters.length; + + dt = dt || that.fixedTimeStep; + + if( numEmitters === 0 ) { + return; + } + + for( var i = 0; i < numEmitters; ++i ) { + emitters[i].tick( dt ); + } + + that._flagUpdate(); + return that; + }, + + + /** + * Fetch a single emitter instance from the pool. + * If there are no objects in the pool, a new emitter will be + * created if specified. + * + * @return {ShaderParticleEmitter | null} + */ + getFromPool: function() { + var that = this, + pool = that._pool, + createNew = that._createNewWhenPoolEmpty; + + if( pool.length ) { + return pool.pop(); + } + else if( createNew ) { + return new SPE.Emitter( that._poolCreationSettings ); + } + + return null; + }, + + + /** + * Release an emitter into the pool. + * + * @param {ShaderParticleEmitter} emitter + * @return {this} + */ + releaseIntoPool: function( emitter ) { + if( !(emitter instanceof SPE.Emitter) ) { + console.error( 'Will not add non-emitter to particle group pool:', emitter ); + return; + } + + emitter.reset(); + this._pool.unshift( emitter ); + + return this; + }, + + + /** + * Get the pool array + * + * @return {Array} + */ + getPool: function() { + return this._pool; + }, + + + /** + * Add a pool of emitters to this particle group + * + * @param {Number} numEmitters The number of emitters to add to the pool. + * @param {Object} emitterSettings An object describing the settings to pass to each emitter. + * @param {Boolean} createNew Should a new emitter be created if the pool runs out? + * @return {this} + */ + addPool: function( numEmitters, emitterSettings, createNew ) { + var that = this, + emitter; + + // Save relevant settings and flags. + that._poolCreationSettings = emitterSettings; + that._createNewWhenPoolEmpty = !!createNew; + + // Create the emitters, add them to this group and the pool. + for( var i = 0; i < numEmitters; ++i ) { + emitter = new SPE.Emitter( emitterSettings ); + that.addEmitter( emitter ); + that.releaseIntoPool( emitter ); + } + + return that; + }, + + + /** + * Internal method. Sets a single emitter to be alive + * + * @private + * + * @param {THREE.Vector3} pos + * @return {this} + */ + _triggerSingleEmitter: function( pos ) { + var that = this, + emitter = that.getFromPool(); + + if( emitter === null ) { + console.log('SPE.Group pool ran out.'); + return; + } + + // TODO: Should an instanceof check happen here? Or maybe at least a typeof? + if( pos ) { + emitter.position.copy( pos ); + } + + emitter.enable(); + + setTimeout( function() { + emitter.disable(); + that.releaseIntoPool( emitter ); + }, that.maxAgeMilliseconds ); + + return that; + }, + + + /** + * Set a given number of emitters as alive, with an optional position + * vector3 to move them to. + * + * @param {Number} numEmitters + * @param {THREE.Vector3} position + * @return {this} + */ + triggerPoolEmitter: function( numEmitters, position ) { + var that = this; + + if( typeof numEmitters === 'number' && numEmitters > 1) { + for( var i = 0; i < numEmitters; ++i ) { + that._triggerSingleEmitter( position ); + } + } + else { + that._triggerSingleEmitter( position ); + } + + return that; + } +}; + + +// Extend ShaderParticleGroup's prototype with functions from utils object. +for( var i in SPE.utils ) { + SPE.Group.prototype[ '_' + i ] = SPE.utils[i]; +} + + +// The all-important shaders +SPE.shaders = { + vertex: [ + 'uniform float duration;', + 'uniform int hasPerspective;', + + 'attribute vec3 colorStart;', + 'attribute vec3 colorMiddle;', + 'attribute vec3 colorEnd;', + 'attribute vec3 opacity;', + + 'attribute vec3 acceleration;', + 'attribute vec3 velocity;', + 'attribute float alive;', + 'attribute float age;', + + 'attribute vec3 size;', + 'attribute vec4 angle;', + + // values to be passed to the fragment shader + 'varying vec4 vColor;', + 'varying float vAngle;', + + + // Integrate acceleration into velocity and apply it to the particle's position + 'vec4 GetPos() {', + 'vec3 newPos = vec3( position );', + + // Move acceleration & velocity vectors to the value they + // should be at the current age + 'vec3 a = acceleration * age;', + 'vec3 v = velocity * age;', + + // Move velocity vector to correct values at this age + 'v = v + (a * age);', + + // Add velocity vector to the newPos vector + 'newPos = newPos + v;', + + // Convert the newPos vector into world-space + 'vec4 mvPosition = modelViewMatrix * vec4( newPos, 1.0 );', + + 'return mvPosition;', + '}', + + + 'void main() {', + + 'float positionInTime = (age / duration);', + + 'float lerpAmount1 = (age / (0.5 * duration));', // percentage during first half + 'float lerpAmount2 = ((age - 0.5 * duration) / (0.5 * duration));', // percentage during second half + 'float halfDuration = duration / 2.0;', + 'float pointSize = 0.0;', + + 'vAngle = 0.0;', + + 'if( alive > 0.5 ) {', + + // lerp the color and opacity + 'if( positionInTime < 0.5 ) {', + 'vColor = vec4( mix(colorStart, colorMiddle, lerpAmount1), mix(opacity.x, opacity.y, lerpAmount1) );', + '}', + 'else {', + 'vColor = vec4( mix(colorMiddle, colorEnd, lerpAmount2), mix(opacity.y, opacity.z, lerpAmount2) );', + '}', + + + // Get the position of this particle so we can use it + // when we calculate any perspective that might be required. + 'vec4 pos = GetPos();', + + + // Determine the angle we should use for this particle. + 'if( angle[3] == 1.0 ) {', + 'vAngle = -atan(pos.y, pos.x);', + '}', + 'else if( positionInTime < 0.5 ) {', + 'vAngle = mix( angle.x, angle.y, lerpAmount1 );', + '}', + 'else {', + 'vAngle = mix( angle.y, angle.z, lerpAmount2 );', + '}', + + // Determine point size. + 'if( positionInTime < 0.5) {', + 'pointSize = mix( size.x, size.y, lerpAmount1 );', + '}', + 'else {', + 'pointSize = mix( size.y, size.z, lerpAmount2 );', + '}', + + + 'if( hasPerspective == 1 ) {', + 'pointSize = pointSize * ( 300.0 / length( pos.xyz ) );', + '}', + + // Set particle size and position + 'gl_PointSize = pointSize;', + 'gl_Position = projectionMatrix * pos;', + '}', + + 'else {', + // Hide particle and set its position to the (maybe) glsl + // equivalent of Number.POSITIVE_INFINITY + 'vColor = vec4( 0.0, 0.0, 0.0, 0.0 );', + 'gl_Position = vec4(1000000000.0, 1000000000.0, 1000000000.0, 0.0);', + '}', + '}', + ].join('\n'), + + fragment: [ + 'uniform sampler2D texture;', + 'uniform int colorize;', + + 'varying vec4 vColor;', + 'varying float vAngle;', + + 'void main() {', + 'float c = cos(vAngle);', + 'float s = sin(vAngle);', + + 'vec2 rotatedUV = vec2(c * (gl_PointCoord.x - 0.5) + s * (gl_PointCoord.y - 0.5) + 0.5,', + 'c * (gl_PointCoord.y - 0.5) - s * (gl_PointCoord.x - 0.5) + 0.5);', + + 'vec4 rotatedTexture = texture2D( texture, rotatedUV );', + + 'if( colorize == 1 ) {', + 'gl_FragColor = vColor * rotatedTexture;', + '}', + 'else {', + 'gl_FragColor = rotatedTexture;', + '}', + '}' + ].join('\n') +}; +; + +// ShaderParticleEmitter 0.7.9 +// +// (c) 2014 Luke Moody (http://www.github.com/squarefeet) +// & Lee Stemkoski (http://www.adelphi.edu/~stemkoski/) +// +// Based on Lee Stemkoski's original work: +// (https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/js/ParticleEngine.js). +// +// ShaderParticleEmitter may be freely distributed under the MIT license (See LICENSE.txt) + +var SPE = SPE || {}; + +SPE.Emitter = function( options ) { + // If no options are provided, fallback to an empty object. + options = options || {}; + + // Helps with minification. Not as easy to read the following code, + // but should still be readable enough! + var that = this; + + + that.particleCount = typeof options.particleCount === 'number' ? options.particleCount : 100; + that.type = (options.type === 'cube' || options.type === 'sphere' || options.type === 'disk') ? options.type : 'cube'; + + that.position = options.position instanceof THREE.Vector3 ? options.position : new THREE.Vector3(); + that.positionSpread = options.positionSpread instanceof THREE.Vector3 ? options.positionSpread : new THREE.Vector3(); + + // These two properties are only used when this.type === 'sphere' or 'disk' + that.radius = typeof options.radius === 'number' ? options.radius : 10; + that.radiusSpread = typeof options.radiusSpread === 'number' ? options.radiusSpread : 0; + that.radiusScale = options.radiusScale instanceof THREE.Vector3 ? options.radiusScale : new THREE.Vector3(1, 1, 1); + that.radiusSpreadClamp = typeof options.radiusSpreadClamp === 'number' ? options.radiusSpreadClamp : 0; + + that.acceleration = options.acceleration instanceof THREE.Vector3 ? options.acceleration : new THREE.Vector3(); + that.accelerationSpread = options.accelerationSpread instanceof THREE.Vector3 ? options.accelerationSpread : new THREE.Vector3(); + + that.velocity = options.velocity instanceof THREE.Vector3 ? options.velocity : new THREE.Vector3(); + that.velocitySpread = options.velocitySpread instanceof THREE.Vector3 ? options.velocitySpread : new THREE.Vector3(); + + + // And again here; only used when this.type === 'sphere' or 'disk' + that.speed = parseFloat( typeof options.speed === 'number' ? options.speed : 0.0 ); + that.speedSpread = parseFloat( typeof options.speedSpread === 'number' ? options.speedSpread : 0.0 ); + + + // Sizes + that.sizeStart = parseFloat( typeof options.sizeStart === 'number' ? options.sizeStart : 1.0 ); + that.sizeStartSpread = parseFloat( typeof options.sizeStartSpread === 'number' ? options.sizeStartSpread : 0.0 ); + + that.sizeEnd = parseFloat( typeof options.sizeEnd === 'number' ? options.sizeEnd : that.sizeStart ); + that.sizeEndSpread = parseFloat( typeof options.sizeEndSpread === 'number' ? options.sizeEndSpread : 0.0 ); + + that.sizeMiddle = parseFloat( + typeof options.sizeMiddle !== 'undefined' ? + options.sizeMiddle : + Math.abs(that.sizeEnd + that.sizeStart) / 2 + ); + that.sizeMiddleSpread = parseFloat( typeof options.sizeMiddleSpread === 'number' ? options.sizeMiddleSpread : 0 ); + + + // Angles + that.angleStart = parseFloat( typeof options.angleStart === 'number' ? options.angleStart : 0 ); + that.angleStartSpread = parseFloat( typeof options.angleStartSpread === 'number' ? options.angleStartSpread : 0 ); + + that.angleEnd = parseFloat( typeof options.angleEnd === 'number' ? options.angleEnd : 0 ); + that.angleEndSpread = parseFloat( typeof options.angleEndSpread === 'number' ? options.angleEndSpread : 0 ); + + that.angleMiddle = parseFloat( + typeof options.angleMiddle !== 'undefined' ? + options.angleMiddle : + Math.abs(that.angleEnd + that.angleStart) / 2 + ); + that.angleMiddleSpread = parseFloat( typeof options.angleMiddleSpread === 'number' ? options.angleMiddleSpread : 0 ); + + that.angleAlignVelocity = options.angleAlignVelocity || false; + + + // Colors + that.colorStart = options.colorStart instanceof THREE.Color ? options.colorStart : new THREE.Color( 'white' ); + that.colorStartSpread = options.colorStartSpread instanceof THREE.Vector3 ? options.colorStartSpread : new THREE.Vector3(); + + that.colorEnd = options.colorEnd instanceof THREE.Color ? options.colorEnd : that.colorStart.clone(); + that.colorEndSpread = options.colorEndSpread instanceof THREE.Vector3 ? options.colorEndSpread : new THREE.Vector3(); + + that.colorMiddle = + options.colorMiddle instanceof THREE.Color ? + options.colorMiddle : + new THREE.Color().addColors( that.colorStart, that.colorEnd ).multiplyScalar( 0.5 ); + that.colorMiddleSpread = options.colorMiddleSpread instanceof THREE.Vector3 ? options.colorMiddleSpread : new THREE.Vector3(); + + + + // Opacities + that.opacityStart = parseFloat( typeof options.opacityStart !== 'undefined' ? options.opacityStart : 1 ); + that.opacityStartSpread = parseFloat( typeof options.opacityStartSpread !== 'undefined' ? options.opacityStartSpread : 0 ); + + that.opacityEnd = parseFloat( typeof options.opacityEnd === 'number' ? options.opacityEnd : 0 ); + that.opacityEndSpread = parseFloat( typeof options.opacityEndSpread !== 'undefined' ? options.opacityEndSpread : 0 ); + + that.opacityMiddle = parseFloat( + typeof options.opacityMiddle !== 'undefined' ? + options.opacityMiddle : + Math.abs(that.opacityEnd + that.opacityStart) / 2 + ); + that.opacityMiddleSpread = parseFloat( typeof options.opacityMiddleSpread === 'number' ? options.opacityMiddleSpread : 0 ); + + + // Generic + that.duration = typeof options.duration === 'number' ? options.duration : null; + that.alive = parseFloat( typeof options.alive === 'number' ? options.alive : 1.0 ); + that.isStatic = typeof options.isStatic === 'number' ? options.isStatic : 0; + + // Particle spawn callback function. + that.onParticleSpawn = typeof options.onParticleSpawn === 'function' ? options.onParticleSpawn : null; + + + // The following properties are used internally, and mostly set when this emitter + // is added to a particle group. + that.particlesPerSecond = 0; + that.attributes = null; + that.vertices = null; + that.verticesIndex = 0; + that.age = 0.0; + that.maxAge = 0.0; + + that.particleIndex = 0.0; + + that.__id = null; + + that.userData = {}; +}; + +SPE.Emitter.prototype = { + + /** + * Reset a particle's position. Accounts for emitter type and spreads. + * + * @private + * + * @param {THREE.Vector3} p + */ + _resetParticle: function( i ) { + var that = this, + type = that.type, + spread = that.positionSpread, + particlePosition = that.vertices[i], + a = that.attributes, + particleVelocity = a.velocity.value[i], + + vSpread = that.velocitySpread, + aSpread = that.accelerationSpread; + + // Optimise for no position spread or radius + if( + ( type === 'cube' && spread.x === 0 && spread.y === 0 && spread.z === 0 ) || + ( type === 'sphere' && that.radius === 0 ) || + ( type === 'disk' && that.radius === 0 ) + ) { + particlePosition.copy( that.position ); + that._randomizeExistingVector3( particleVelocity, that.velocity, vSpread ); + + if( type === 'cube' ) { + that._randomizeExistingVector3( that.attributes.acceleration.value[i], that.acceleration, aSpread ); + } + } + + // If there is a position spread, then get a new position based on this spread. + else if( type === 'cube' ) { + that._randomizeExistingVector3( particlePosition, that.position, spread ); + that._randomizeExistingVector3( particleVelocity, that.velocity, vSpread ); + that._randomizeExistingVector3( that.attributes.acceleration.value[i], that.acceleration, aSpread ); + } + + else if( type === 'sphere') { + that._randomizeExistingVector3OnSphere( particlePosition, that.position, that.radius, that.radiusSpread, that.radiusScale, that.radiusSpreadClamp ); + that._randomizeExistingVelocityVector3OnSphere( particleVelocity, that.position, particlePosition, that.speed, that.speedSpread ); + } + + else if( type === 'disk') { + that._randomizeExistingVector3OnDisk( particlePosition, that.position, that.radius, that.radiusSpread, that.radiusScale, that.radiusSpreadClamp ); + that._randomizeExistingVelocityVector3OnSphere( particleVelocity, that.position, particlePosition, that.speed, that.speedSpread ); + } + + if( typeof that.onParticleSpawn === 'function' ) { + that.onParticleSpawn( a, i ); + } + }, + + /** + * Update this emitter's particle's positions. Called by the SPE.Group + * that this emitter belongs to. + * + * @param {Number} dt + */ + tick: function( dt ) { + + if( this.isStatic ) { + return; + } + + // Cache some values for quicker access in loops. + var that = this, + a = that.attributes, + alive = a.alive.value, + age = a.age.value, + start = that.verticesIndex, + particleCount = that.particleCount, + end = start + particleCount, + pps = that.particlesPerSecond * that.alive, + ppsdt = pps * dt, + m = that.maxAge, + emitterAge = that.age, + duration = that.duration, + pIndex = that.particleIndex; + + // Loop through all the particles in this emitter and + // determine whether they're still alive and need advancing + // or if they should be dead and therefore marked as such. + for( var i = start; i < end; ++i ) { + if( alive[ i ] === 1.0 ) { + age[ i ] += dt; + } + + if( age[ i ] >= m ) { + age[ i ] = 0.0; + alive[ i ] = 0.0; + } + } + + // If the emitter is dead, reset any particles that are in + // the recycled vertices array and reset the age of the + // emitter to zero ready to go again if required, then + // exit this function. + if( that.alive === 0.0 ) { + that.age = 0.0; + return; + } + + // If the emitter has a specified lifetime and we've exceeded it, + // mark the emitter as dead and exit this function. + if( typeof duration === 'number' && emitterAge > duration ) { + that.alive = 0.0; + that.age = 0.0; + return; + } + + + + var n = Math.max( Math.min( end, pIndex + ppsdt ), 0), + count = 0, + index = 0, + pIndexFloor = pIndex | 0, + dtInc; + + for( i = pIndexFloor; i < n; ++i ) { + if( alive[ i ] !== 1.0 ) { + ++count; + } + } + + if( count !== 0 ) { + dtInc = dt / count; + + for( i = pIndexFloor; i < n; ++i, ++index ) { + if( alive[ i ] !== 1.0 ) { + alive[ i ] = 1.0; + age[ i ] = dtInc * index; + that._resetParticle( i ); + } + } + } + + that.particleIndex += ppsdt; + + if( that.particleIndex < 0.0 ) { + that.particleIndex = 0.0; + } + + if( pIndex >= start + particleCount ) { + that.particleIndex = parseFloat( start ); + } + + // Add the delta time value to the age of the emitter. + that.age += dt; + + if( that.age < 0.0 ) { + that.age = 0.0; + } + }, + + /** + * Reset this emitter back to its starting position. + * If `force` is truthy, then reset all particles in this + * emitter as well, even if they're currently alive. + * + * @param {Boolean} force + * @return {this} + */ + reset: function( force ) { + var that = this; + + that.age = 0.0; + that.alive = 0; + + if( force ) { + var start = that.verticesIndex, + end = that.verticesIndex + that.particleCount, + a = that.attributes, + alive = a.alive.value, + age = a.age.value; + + for( var i = start; i < end; ++i ) { + alive[ i ] = 0.0; + age[ i ] = 0.0; + } + } + + return that; + }, + + + /** + * Enable this emitter. + */ + enable: function() { + this.alive = 1; + }, + + /** + * Disable this emitter. + */ + disable: function() { + this.alive = 0; + } +}; + +// Extend SPE.Emitter's prototype with functions from utils object. +for( var i in SPE.utils ) { + SPE.Emitter.prototype[ '_' + i ] = SPE.utils[i]; +} diff --git a/vendor/scripts/checkout.js b/vendor/scripts/checkout.js deleted file mode 100644 index 2693d8413..000000000 --- a/vendor/scripts/checkout.js +++ /dev/null @@ -1,3 +0,0 @@ -if(typeof JSON!=="object"){JSON={}}(function(){"use strict";function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()}}var cx,escapable,gap,indent,meta,rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){if(typeof rep[i]==="string"){k=rep[i];v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else if(typeof space==="string"){indent=space}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}})();(function(){var namespace="StripeCheckout.require".split("."),name=namespace[namespace.length-1],base=this,i;for(i=0;i<namespace.length-1;i++){base=base[namespace[i]]=base[namespace[i]]||{}}if(base[name]===undefined){base[name]=function(){var modules={},cache={};var requireRelative=function(name,root){var path=expand(root,name),indexPath=expand(path,"./index"),module,fn;module=cache[path]||cache[indexPath];if(module){return module}else if(fn=modules[path]||modules[path=indexPath]){module={id:path,exports:{}};cache[path]=module.exports;fn(module.exports,function(name){return require(name,dirname(path))},module);return cache[path]=module.exports}else{throw"module "+name+" not found"}};var expand=function(root,name){var results=[],parts,part;if(/^\.\.?(\/|$)/.test(name)){parts=[root,name].join("/").split("/")}else{parts=name.split("/")}for(var i=0,length=parts.length;i<length;i++){part=parts[i];if(part==".."){results.pop()}else if(part!="."&&part!=""){results.push(part)}}return results.join("/")};var dirname=function(path){return path.split("/").slice(0,-1).join("/")};var require=function(name){return requireRelative(name,"")};require.define=function(bundle){for(var key in bundle){modules[key]=bundle[key]}};require.modules=modules;require.cache=cache;return require}.call()}})();StripeCheckout.require.define({"vendor/cookie":function(exports,require,module){var cookie={};var pluses=/\+/g;function extend(target,other){target=target||{};for(var prop in other){if(typeof source[prop]==="object"){target[prop]=extend(target[prop],source[prop])}else{target[prop]=source[prop]}}return target}function raw(s){return s}function decoded(s){return decodeURIComponent(s.replace(pluses," "))}function converted(s){if(s.indexOf('"')===0){s=s.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\")}try{return config.json?JSON.parse(s):s}catch(er){}}var config=cookie.set=cookie.get=function(key,value,options){if(value!==undefined){options=extend(options,config.defaults);if(typeof options.expires==="number"){var days=options.expires,t=options.expires=new Date;t.setDate(t.getDate()+days)}value=config.json?JSON.stringify(value):String(value);return document.cookie=[config.raw?key:encodeURIComponent(key),"=",config.raw?value:encodeURIComponent(value),options.expires?"; expires="+options.expires.toUTCString():"",options.path?"; path="+options.path:"",options.domain?"; domain="+options.domain:"",options.secure?"; secure":""].join("")}var decode=config.raw?raw:decoded;var cookies=document.cookie.split("; ");var result=key?undefined:{};for(var i=0,l=cookies.length;i<l;i++){var parts=cookies[i].split("=");var name=decode(parts.shift());var cookie=decode(parts.join("="));if(key&&key===name){result=converted(cookie);break}if(!key){result[name]=converted(cookie)}}return result};config.defaults={};cookie.remove=function(key,options){if(cookie.get(key)!==undefined){cookie.set(key,"",extend(options,{expires:-1}));return true}return false};module.exports=cookie}});StripeCheckout.require.define({"vendor/ready":function(exports,require,module){!function(name,definition){if(typeof module!="undefined")module.exports=definition();else if(typeof define=="function"&&typeof define.amd=="object")define(definition);else this[name]=definition()}("domready",function(ready){var fns=[],fn,f=false,doc=document,testEl=doc.documentElement,hack=testEl.doScroll,domContentLoaded="DOMContentLoaded",addEventListener="addEventListener",onreadystatechange="onreadystatechange",readyState="readyState",loadedRgx=hack?/^loaded|^c/:/^loaded|c/,loaded=loadedRgx.test(doc[readyState]);function flush(f){loaded=1;while(f=fns.shift())f()}doc[addEventListener]&&doc[addEventListener](domContentLoaded,fn=function(){doc.removeEventListener(domContentLoaded,fn,f);flush()},f);hack&&doc.attachEvent(onreadystatechange,fn=function(){if(/^c/.test(doc[readyState])){doc.detachEvent(onreadystatechange,fn);flush()}});return ready=hack?function(fn){self!=top?loaded?fn():fns.push(fn):function(){try{testEl.doScroll("left")}catch(e){return setTimeout(function(){ready(fn)},50)}fn()}()}:function(fn){loaded?fn():fns.push(fn)}})}});(function(){if(!Array.prototype.indexOf){Array.prototype.indexOf=function(obj,start){var f,i,j,_i;j=this.length;f=start?start:0;for(i=_i=f;f<=j?_i<j:_i>j;i=f<=j?++_i:--_i){if(this[i]===obj){return i}}return-1}}}).call(this);StripeCheckout.require.define({"lib/helpers":function(exports,require,module){(function(){var delurkWinPhone,helpers,uaVersionFn;uaVersionFn=function(re){return function(){var uaMatch;uaMatch=helpers.userAgent.match(re);return uaMatch&&parseInt(uaMatch[1])}};delurkWinPhone=function(fn){return function(){return fn()&&!helpers.isWindowsPhone()}};helpers={userAgent:window.navigator.userAgent,escape:function(value){return value&&(""+value).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/\"/g,""")},trim:function(value){return value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")},sanitizeURL:function(value){var SCHEME_WHITELIST,allowed,scheme,_i,_len;if(!value){return}value=helpers.trim(value);SCHEME_WHITELIST=["data:","http:","https:"];allowed=false;for(_i=0,_len=SCHEME_WHITELIST.length;_i<_len;_i++){scheme=SCHEME_WHITELIST[_i];if(value.indexOf(scheme)===0){allowed=true;break}}if(!allowed){return null}return encodeURI(value)},iOSVersion:uaVersionFn(/(?:iPhone OS |iPad; CPU OS )(\d+)_\d+/),androidWebkitVersion:uaVersionFn(/Mozilla\/5\.0.*Android.*AppleWebKit\/([\d]+)/),androidVersion:uaVersionFn(/Android (\d+)\.\d+/),firefoxVersion:uaVersionFn(/Firefox\/(\d+)\.\d+/),chromeVersion:uaVersionFn(/Chrome\/(\d+)\.\d+/),safariVersion:uaVersionFn(/Version\/(\d+)\.\d+ Safari/),iOSChromeVersion:uaVersionFn(/CriOS\/(\d+)\.\d+/),ieVersion:uaVersionFn(/(?:MSIE |Trident\/.*rv:)(\d{1,2})\./),isiOSChrome:function(){return/CriOS/.test(helpers.userAgent)},isiOSWebView:function(){return/(iPhone|iPod|iPad).*AppleWebKit((?!.*Safari)|(.*\([^)]*like[^)]*Safari[^)]*\)))/i.test(helpers.userAgent)},isiOS:delurkWinPhone(function(){return/(iPhone|iPad|iPod)/i.test(helpers.userAgent)}),isiPad:function(){return/(iPad)/i.test(helpers.userAgent)},isMac:delurkWinPhone(function(){return/mac/i.test(helpers.userAgent)}),isWindowsPhone:function(){return/(Windows\sPhone|IEMobile)/i.test(helpers.userAgent)},isWindowsOS:function(){return/(Windows NT \d\.\d)/i.test(helpers.userAgent)},isIE:function(){return/(MSIE ([0-9]{1,}[\.0-9]{0,})|Trident\/)/i.test(helpers.userAgent)},isChrome:function(){return"chrome"in window},isSafari:delurkWinPhone(function(){var userAgent;userAgent=helpers.userAgent;return/Safari/i.test(userAgent)&&!/Chrome/i.test(userAgent)}),isFirefox:delurkWinPhone(function(){return helpers.firefoxVersion()!=null}),isAndroidBrowser:function(){var version;version=helpers.androidWebkitVersion();return version&&version<537},isAndroidChrome:function(){var version;version=helpers.androidWebkitVersion();return version&&version>=537},isAndroidDevice:delurkWinPhone(function(){return/Android/.test(helpers.userAgent)}),isSupportedMobileOS:function(){return helpers.isiOS()||helpers.isAndroidDevice()},isAndroidWebapp:function(){var metaTag;if(!helpers.isAndroidChrome()){return false}metaTag=(document.getElementsByName("apple-mobile-web-app-capable")||document.getElementsByName("mobile-web-app-capable"))[0];return metaTag&&metaTag.content==="yes"},isInsideFrame:function(){return window.top!==window.self},isFallback:function(){var androidVersion,criosVersion,ffVersion,iOSVersion;if(!("postMessage"in window)||window.postMessageDisabled||document.documentMode&&document.documentMode<8){return true}androidVersion=helpers.androidVersion();if(androidVersion&&androidVersion<4){return true}iOSVersion=helpers.iOSVersion();if(iOSVersion&&iOSVersion<6){return true}ffVersion=helpers.firefoxVersion();if(ffVersion&&ffVersion<11){return true}criosVersion=helpers.iOSChromeVersion();if(criosVersion&&criosVersion<36){return true}return false},isSmallScreen:function(){return Math.min(window.screen.availHeight,window.screen.availWidth)<=640||/FakeCheckoutMobile/.test(helpers.userAgent)},pad:function(number,width,padding){var leading;if(width==null){width=2}if(padding==null){padding="0"}number=number+"";if(number.length>width){return number}leading=new Array(width-number.length+1).join(padding);return leading+number},requestAnimationFrame:function(callback){return(typeof window.requestAnimationFrame==="function"?window.requestAnimationFrame(callback):void 0)||(typeof window.webkitRequestAnimationFrame==="function"?window.webkitRequestAnimationFrame(callback):void 0)||window.setTimeout(callback,100)},requestAnimationInterval:function(func,interval){var callback,previous;previous=new Date;callback=function(){var frame,now,remaining;frame=helpers.requestAnimationFrame(callback);now=new Date;remaining=interval-(now-previous);if(remaining<=0){previous=now;func()}return frame};return callback()},getQueryParameterByName:function(name){var match;match=RegExp("[?&]"+name+"=([^&]*)").exec(window.location.search);return match&&decodeURIComponent(match[1].replace(/\+/g," "))},addQueryParameter:function(url,name,value){var hashParts,query;query=encodeURIComponent(name)+"="+encodeURIComponent(value);hashParts=new String(url).split("#");hashParts[0]+=hashParts[0].indexOf("?")!==-1?"&":"?";hashParts[0]+=query;return hashParts.join("#")},bind:function(element,name,callback){if(element.addEventListener){return element.addEventListener(name,callback,false)}else{return element.attachEvent("on"+name,callback)}},unbind:function(element,name,callback){if(element.removeEventListener){return element.removeEventListener(name,callback,false)}else{return element.detachEvent("on"+name,callback)}},host:function(url){var parent,parser;parent=document.createElement("div");parent.innerHTML='<a href="'+this.escape(url)+'">x</a>';parser=parent.firstChild;return""+parser.protocol+"//"+parser.host},strip:function(html){var tmp,_ref,_ref1;tmp=document.createElement("div");tmp.innerHTML=html;return(_ref=(_ref1=tmp.textContent)!=null?_ref1:tmp.innerText)!=null?_ref:""},setAutocomplete:function(el,type){var secureCCFill;secureCCFill=helpers.chromeVersion()>14||helpers.safariVersion()>7;if(type!=="cc-csc"&&(!/^cc-/.test(type)||secureCCFill)){el.setAttribute("x-autocompletetype",type);el.setAttribute("autocompletetype",type)}else{el.setAttribute("autocomplete","off")}if(!(type==="country-name"||type==="language"||type==="sex"||type==="gender-identity")){el.setAttribute("autocorrect","off");el.setAttribute("spellcheck","off")}if(!(/name|honorific/.test(type)||(type==="locality"||type==="city"||type==="adminstrative-area"||type==="state"||type==="province"||type==="region"||type==="language"||type==="org"||type==="organization-title"||type==="sex"||type==="gender-identity"))){return el.setAttribute("autocapitalize","off")}},hashCode:function(str){var hash,i,_i,_ref;hash=5381;for(i=_i=0,_ref=str.length;_i<_ref;i=_i+=1){hash=(hash<<5)+hash+str.charCodeAt(i)}return(hash>>>0)%65535},stripeUrlPrefix:function(){var match;match=window.location.hostname.match("^([a-z-]*)checkout.");if(match){return match[1]}else{return""}}};module.exports=helpers}).call(this)}});StripeCheckout.require.define({"lib/rpc":function(exports,require,module){(function(){var RPC,helpers,tracker,__bind=function(fn,me){return function(){return fn.apply(me,arguments)}},__slice=[].slice;helpers=require("lib/helpers");tracker=require("lib/tracker");RPC=function(){function RPC(target,options){if(options==null){options={}}this.processMessage=__bind(this.processMessage,this);this.sendMessage=__bind(this.sendMessage,this);this.invoke=__bind(this.invoke,this);this.startSession=__bind(this.startSession,this);if(options.host==null){throw new Error("Host should be provided")}this.rpcID=0;this.target=target;this.host=options.host;this.callbacks={};this.readyQueue=[];this.readyStatus=false;this.methods={};helpers.bind(window,"message",function(_this){return function(){var args;args=1<=arguments.length?__slice.call(arguments,0):[];return _this.message.apply(_this,args)}}(this))}RPC.prototype.startSession=function(){this.sendMessage("frameReady");return this.frameReady()};RPC.prototype.invoke=function(){var args,method;method=arguments[0],args=2<=arguments.length?__slice.call(arguments,1):[];tracker.trace.rpcInvoke(method);return this.ready(function(_this){return function(){return _this.sendMessage(method,args)}}(this))};RPC.prototype.message=function(e){var shouldProcess;shouldProcess=false;try{shouldProcess=e.source===this.target||e.source===window}catch(_error){}if(shouldProcess){return this.processMessage(e.data)}};RPC.prototype.ready=function(fn){if(this.readyStatus){return fn()}else{return this.readyQueue.push(fn)}};RPC.prototype.frameCallback=function(id,result){var _base;if(typeof(_base=this.callbacks)[id]==="function"){_base[id](result)}delete this.callbacks[id];return true};RPC.prototype.frameReady=function(){var callbacks,cb,_i,_len;this.readyStatus=true;callbacks=this.readyQueue.slice(0);for(_i=0,_len=callbacks.length;_i<_len;_i++){cb=callbacks[_i];cb()}return false};RPC.prototype.isAlive=function(){return true};RPC.prototype.sendMessage=function(method,args){var err,id,message,_ref;if(args==null){args=[]}id=++this.rpcID;if(typeof args[args.length-1]==="function"){this.callbacks[id]=args.pop()}message=JSON.stringify({method:method,args:args,id:id});if(((_ref=this.target)!=null?_ref.postMessage:void 0)==null){err=new Error("Unable to communicate with Checkout. Please contact support@stripe.com if the problem persists.");if(this.methods.rpcError!=null){this.methods.rpcError(err)}else{throw err}return}this.target.postMessage(message,"*");return tracker.trace.rpcPostMessage(method,args,id)};RPC.prototype.processMessage=function(data){var method,result,_base,_name;try{data=JSON.parse(data)}catch(_error){return}if(["frameReady","frameCallback","isAlive"].indexOf(data.method)!==-1){result=null;method=this[data.method];if(method!=null){result=method.apply(this,data.args)}}else{result=typeof(_base=this.methods)[_name=data.method]==="function"?_base[_name].apply(_base,data.args):void 0}if(data.method!=="frameCallback"){return this.invoke("frameCallback",data.id,result)}};return RPC}();module.exports=RPC}).call(this)}});StripeCheckout.require.define({"lib/uuid":function(exports,require,module){(function(){var S4;S4=function(){return((1+Math.random())*65536|0).toString(16).substring(1)};module.exports.generate=function(){var delim;delim="-";return S4()+S4()+delim+S4()+delim+S4()+delim+S4()+delim+S4()+S4()+S4()}}).call(this)}});StripeCheckout.require.define({"lib/pixel":function(exports,require,module){(function(){var encode,generateID,getCookie,getCookieID,getLocalStorageID,request,setCookie,track;generateID=function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(c){var r,v;r=Math.random()*16|0;v=c==="x"?r:r&3|8;return v.toString(16)})};setCookie=function(name,value,options){var cookie,expires;if(options==null){options={}}if(options.expires===true){options.expires=-1}if(typeof options.expires==="number"){expires=new Date;expires.setTime(expires.getTime()+options.expires*24*60*60*1e3);options.expires=expires}if(options.path==null){options.path="/"}value=(value+"").replace(/[^!#-+\--:<-\[\]-~]/g,encodeURIComponent);cookie=encodeURIComponent(name)+"="+value;if(options.expires){cookie+=";expires="+options.expires.toGMTString()}if(options.path){cookie+=";path="+options.path}if(options.domain){cookie+=";domain="+options.domain}return document.cookie=cookie};getCookie=function(name){var cookie,cookies,index,key,value,_i,_len;cookies=document.cookie.split("; ");for(_i=0,_len=cookies.length;_i<_len;_i++){cookie=cookies[_i];index=cookie.indexOf("=");key=decodeURIComponent(cookie.substr(0,index));value=decodeURIComponent(cookie.substr(index+1));if(key===name){return value}}return null};encode=function(param){if(typeof param==="string"){return encodeURIComponent(param)}else{return encodeURIComponent(JSON.stringify(param))}};request=function(url,params,callback){var image,k,v;if(params==null){params={}}params.i=(new Date).getTime();params=function(){var _results;_results=[];for(k in params){v=params[k];_results.push(""+k+"="+encode(v))}return _results}().join("&");image=new Image;if(callback){image.onload=callback}image.src=""+url+"?"+params;return true};getLocalStorageID=function(){var err,lsid;if(window.navigator.doNotTrack){return"DNT"}try{lsid=localStorage.getItem("lsid");if(!lsid){lsid=generateID();localStorage.setItem("lsid",lsid)}return lsid}catch(_error){err=_error;return"NA"}};getCookieID=function(){var id;if(window.navigator.doNotTrack){return"DNT"}id=getCookie("cid")||generateID();setCookie("cid",id,{expires:360*20,domain:".stripe.com"});return id};track=function(event,params,callback){var k,referrer,request_params,search,v;if(params==null){params={}}referrer=document.referrer;search=window.location.search;request_params={event:event,rf:referrer,sc:search};for(k in params){v=params[k];request_params[k]=v}request_params.lsid||(request_params.lsid=getLocalStorageID());request_params.cid||(request_params.cid=getCookieID());return request("https://q.stripe.com",request_params,callback)};module.exports.track=track;module.exports.getLocalStorageID=getLocalStorageID;module.exports.getCookieID=getCookieID}).call(this)}});StripeCheckout.require.define({"vendor/base64":function(exports,require,module){var utf8Encode=function(string){string=(string+"").replace(/\r\n/g,"\n").replace(/\r/g,"\n");var utftext="",start,end;var stringl=0,n;start=end=0;stringl=string.length;for(n=0;n<stringl;n++){var c1=string.charCodeAt(n);var enc=null;if(c1<128){end++}else if(c1>127&&c1<2048){enc=String.fromCharCode(c1>>6|192,c1&63|128)}else{enc=String.fromCharCode(c1>>12|224,c1>>6&63|128,c1&63|128)}if(enc!==null){if(end>start){utftext+=string.substring(start,end)}utftext+=enc;start=end=n+1}}if(end>start){utftext+=string.substring(start,string.length)}return utftext};module.exports.encode=function(data){var b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var o1,o2,o3,h1,h2,h3,h4,bits,i=0,ac=0,enc="",tmp_arr=[];if(!data){return data}data=utf8Encode(data);do{o1=data.charCodeAt(i++);o2=data.charCodeAt(i++);o3=data.charCodeAt(i++);bits=o1<<16|o2<<8|o3;h1=bits>>18&63;h2=bits>>12&63;h3=bits>>6&63;h4=bits&63;tmp_arr[ac++]=b64.charAt(h1)+b64.charAt(h2)+b64.charAt(h3)+b64.charAt(h4)}while(i<data.length);enc=tmp_arr.join("");switch(data.length%3){case 1:enc=enc.slice(0,-2)+"==";break;case 2:enc=enc.slice(0,-1)+"=";break}return enc}}});StripeCheckout.require.define({"lib/tracker":function(exports,require,module){(function(){var base64,config,isEventNameExisting,mixpanel,pixel,stateParameters,trace,traceSerialize,track,tracker,uuid,__indexOf=[].indexOf||function(item){for(var i=0,l=this.length;i<l;i++){if(i in this&&this[i]===item)return i}return-1};uuid=require("lib/uuid");pixel=require("lib/pixel");base64=require("vendor/base64");config={enabled:false,tracingEnabled:false,eventNamePrefix:"checkout.",distinctId:uuid.generate(),mixpanelKey:null};stateParameters={};tracker={};tracker.setEnabled=function(enabled){return config.enabled=enabled};tracker.setTracingEnabled=function(enabled){return config.tracingEnabled=enabled};tracker.setMixpanelKey=function(mixpanelKey){return config.mixpanelKey=mixpanelKey};tracker.track={outerOpen:function(parameters){var requiredKeys;requiredKeys=["key"];return track("outer.open",parameters,requiredKeys,{appendStateParameters:false})},open:function(options){var k,parameters,v;parameters={};for(k in options){v=options[k];parameters["option-"+k]=v}return track("open",parameters)},close:function(parameters){return track("close",parameters,["withToken"])},rememberMe:function(parameters){return track("checkbox.rememberMe",parameters,["checked"])},authorizeAccount:function(){return track("account.authorize")},login:function(){return track("account.authorize.success")},wrongVerificationCode:function(){return track("account.authorize.fail")},keepMeLoggedIn:function(parameters){return track("checkbox.keepMeLoggedIn",parameters,["checked"])},logout:function(){return track("account.logout")},submit:function(){return track("submit")},invalid:function(parameters){if(parameters["err"]==null&¶meters["fields"]==null){throw new Error("Cannot track invalid because err or fields should be provided")}return track("invalid",parameters)},moreInfo:function(){return track("moreInfoLink.click")},accountCreateSuccess:function(){return track("account.create.success")},accountCreateFail:function(){return track("account.create.fail")},addressAutocompleteShow:function(){return track("addressAutoComplete.show")},addressAutocompleteResultSelected:function(){return track("addressAutocomplete.result.selected")},back:function(parameters){return track("back",parameters,["from_step","to_step"])},token:function(parameters){return track("token",parameters,["stripe_token"])},phoneVerificationShow:function(){return track("phoneVerification.show")},phoneVerificationCreate:function(parameters){return track("phoneVerification.create",parameters,["use_sms"])},phoneVerificationAuthorize:function(parameters){return track("fraudCodeVerification.authorize",parameters,["valid"])}};tracker.trace={trigger:function(eventName,args){var EXCLUDED_EVENTS;EXCLUDED_EVENTS=["didResize","viewAddedToDOM","valueDidChange","checkedDidChange","keyUp","keyDown","keyPress","keyInput","click","blur"];eventName=eventName.split(".");if(eventName[eventName.length-1]==="checkout"){eventName.pop()}eventName=eventName.join(".");if(__indexOf.call(EXCLUDED_EVENTS,eventName)<0){if(this._triggerQueue==null){this._triggerQueue={}}this._triggerQueue[eventName]=traceSerialize(args);return this._triggerTimeout!=null?this._triggerTimeout:this._triggerTimeout=setTimeout(function(_this){return function(){var _ref;_ref=_this._triggerQueue;for(eventName in _ref){args=_ref[eventName];trace("trigger."+eventName,{args:args})}_this._triggerQueue={};return _this._triggerTimeout=null}}(this),0)}},rpcInvoke:function(method){return trace("rpc.invoke."+method)},rpcPostMessage:function(method,args,id){return trace("rpc.postMessage."+method,{id:id,args:traceSerialize(args)})}};tracker.state={setUIType:function(type){return stateParameters["st-ui-type"]=type},setUIIntegration:function(integration){return stateParameters["st-ui-integration"]=integration},setAccountsEnabled:function(bool){return stateParameters["st-accounts-enabled"]=bool},setRememberMeEnabled:function(bool){return stateParameters["st-remember-me-enabled"]=bool},setRememberMeChecked:function(bool){return stateParameters["st-remember-me-checked"]=bool},setAccountCreated:function(bool){return stateParameters["st-account-created"]=bool},setLoggedIn:function(bool){return stateParameters["st-logged-in"]=bool},setVariants:function(variants){var k,v,_results;_results=[];for(k in variants){v=variants[k];_results.push(stateParameters["st-variant-"+k]=v)}return _results},setPhoneVerificationShown:function(bool){return stateParameters["st-phone-verification-shown"]=bool}};tracker.dontTrack=function(fn){var enabled;enabled=config.enabled;config.enabled=false;fn();return config.enabled=enabled};isEventNameExisting=function(eventName){var exists,k,v,_ref;exists=false;_ref=tracker.events;for(k in _ref){v=_ref[k];if(v===eventName){exists=true;break}}return exists};trace=function(eventName,parameters,requiredKeys,options){if(parameters==null){parameters={}}if(requiredKeys==null){requiredKeys=[]}if(options==null){options={}}if(!config.tracingEnabled){return}eventName="trace."+eventName;options.excludeMixpanel=true;return track.apply(this,arguments)};track=function(eventName,parameters,requiredKeys,options){var fullEventName,k,key,missingKeys,v,_i,_len;if(parameters==null){parameters={}}if(requiredKeys==null){requiredKeys=[]}if(options==null){options={}}if(!config.enabled){return}missingKeys=function(){var _i,_len,_results;_results=[];for(_i=0,_len=requiredKeys.length;_i<_len;_i++){key=requiredKeys[_i];if(!(key in parameters)){_results.push(key)}}return _results}();if(missingKeys.length>0){throw new Error("Missing required data ("+missingKeys.join(", ")+") for tracking "+eventName+".")}parameters.distinct_id=config.distinctId;if(options.appendStateParameters==null){options.appendStateParameters=true}if(options.appendStateParameters){for(k in stateParameters){v=stateParameters[k];parameters[k]=v}}parameters.h=screen.height;parameters.w=screen.width;for(v=_i=0,_len=parameters.length;_i<_len;v=++_i){k=parameters[v];if(v instanceof Array){v.sort()}}fullEventName=""+config.eventNamePrefix+eventName;if(!options.excludeMixpanel){mixpanel.track(fullEventName,parameters)}return pixel.track(fullEventName,parameters)};mixpanel={};mixpanel.track=function(eventName,options){var dataStr,properties;if(options==null){options={}}if(!(typeof $!=="undefined"&&$!==null&&config.mixpanelKey!=null)){return}properties=$.extend({token:config.mixpanelKey,userAgent:window.navigator.userAgent},options);delete properties["stripe_token"];dataStr=base64.encode(JSON.stringify({event:eventName,properties:properties}));return(new Image).src="https://api.mixpanel.com/track/?ip=1&img=1&data="+dataStr};traceSerialize=function(value){var k,obj,v;if(value instanceof Array){return JSON.stringify(function(){var _i,_len,_results;_results=[];for(_i=0,_len=value.length;_i<_len;_i++){v=value[_i];_results.push(traceSerialize(v))}return _results}())}else if(value!=null&&value.target!=null&&value.type!=null){return traceSerialize({type:value.type,target_id:value.target.id})}else if(value instanceof Object){if(value.constructor===Object){obj={};for(k in value){v=value[k];obj[k]=traceSerialize(v)}return JSON.stringify(obj)}else{return value.toString()}}else{return value}};module.exports=tracker}).call(this)}});StripeCheckout.require.define({"outer/lib/fallbackRpc":function(exports,require,module){(function(){var FallbackRPC,cacheBust,interval,lastHash,re,__bind=function(fn,me){return function(){return fn.apply(me,arguments)}};cacheBust=1;interval=null;lastHash=null;re=/^#?\d+&/;FallbackRPC=function(){function FallbackRPC(target,host){this.invokeTarget=__bind(this.invokeTarget,this);this.target=target;this.host=host}FallbackRPC.prototype.invokeTarget=function(message){var url;message=+new Date+cacheBust++ +"&"+encodeURIComponent(message);url=this.host+"";return this.target.location=url.replace(/#.*$/,"")+"#"+message};FallbackRPC.prototype.receiveMessage=function(callback,delay){if(delay==null){delay=100}interval&&clearInterval(interval);return interval=setInterval(function(){var hash;hash=decodeURIComponent(window.location.hash);if(hash!==lastHash&&re.test(hash)){window.location.hash="";lastHash=hash;return callback({data:hash.replace(re,"")})}},delay)};return FallbackRPC}();module.exports=FallbackRPC}).call(this)}});StripeCheckout.require.define({"outer/lib/utils":function(exports,require,module){(function(){var $,$$,addClass,append,css,hasAttr,hasClass,insertAfter,insertBefore,parents,remove,resolve,text,trigger,__indexOf=[].indexOf||function(item){for(var i=0,l=this.length;i<l;i++){if(i in this&&this[i]===item)return i}return-1};$=function(sel){return document.querySelectorAll(sel)};$$=function(cls){var el,reg,_i,_len,_ref,_results;if(typeof document.getElementsByClassName==="function"){return document.getElementsByClassName(cls)}else if(typeof document.querySelectorAll==="function"){return document.querySelectorAll("."+cls)}else{reg=new RegExp("(^|\\s)"+cls+"(\\s|$)");_ref=document.getElementsByTagName("*");_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){el=_ref[_i];if(reg.test(el.className)){_results.push(el)}}return _results}};hasAttr=function(element,attr){var node;if(typeof element.hasAttribute==="function"){return element.hasAttribute(attr)}else{node=element.getAttributeNode(attr);return!!(node&&(node.specified||node.nodeValue))}};trigger=function(element,name,data,bubble){if(data==null){data={}}if(bubble==null){bubble=true}if(window.jQuery){return jQuery(element).trigger(name,data)}};addClass=function(element,name){return element.className+=" "+name};hasClass=function(element,name){return __indexOf.call(element.className.split(" "),name)>=0};css=function(element,css){return element.style.cssText+=";"+css};insertBefore=function(element,child){return element.parentNode.insertBefore(child,element)};insertAfter=function(element,child){return element.parentNode.insertBefore(child,element.nextSibling)};append=function(element,child){return element.appendChild(child)};remove=function(element){var _ref;return(_ref=element.parentNode)!=null?_ref.removeChild(element):void 0};parents=function(node){var ancestors; -ancestors=[];while((node=node.parentNode)&&node!==document&&__indexOf.call(ancestors,node)<0){ancestors.push(node)}return ancestors};resolve=function(url){var parser;parser=document.createElement("a");parser.href=url;return""+parser.href};text=function(element,value){if("innerText"in element){element.innerText=value}else{element.textContent=value}return value};module.exports={$:$,$$:$$,hasAttr:hasAttr,trigger:trigger,addClass:addClass,hasClass:hasClass,css:css,insertBefore:insertBefore,insertAfter:insertAfter,append:append,remove:remove,parents:parents,resolve:resolve,text:text}}).call(this)}});StripeCheckout.require.define({"outer/controllers/app":function(exports,require,module){(function(){var App,Checkout,RPC,tracker,utils,__bind=function(fn,me){return function(){return fn.apply(me,arguments)}};Checkout=require("outer/controllers/checkout");RPC=require("lib/rpc");tracker=require("lib/tracker");utils=require("outer/lib/utils");App=function(){function App(options){var _ref,_ref1;if(options==null){options={}}this.setHost=__bind(this.setHost,this);this.configure=__bind(this.configure,this);this.open=__bind(this.open,this);this.configurations={};this.checkouts={};this.host="https://checkout.stripe.com";this.timeLoaded=Math.floor((new Date).getTime()/1e3);this.totalButtons=0;if(((_ref=window.Prototype)!=null?(_ref1=_ref.Version)!=null?_ref1.indexOf("1.6"):void 0:void 0)===0){console.error("Stripe Checkout is not compatible with your version of Prototype.js. Please upgrade to version 1.7 or greater.")}}App.prototype.open=function(options,buttonId){var checkout,k,mergedOptions,v,_ref;if(buttonId==null){buttonId=null}mergedOptions={referrer:document.referrer,url:document.URL,timeLoaded:this.timeLoaded};if(buttonId&&this.configurations[buttonId]){_ref=this.configurations[buttonId];for(k in _ref){v=_ref[k];mergedOptions[k]=v}}for(k in options){v=options[k];mergedOptions[k]=v}if(mergedOptions.image){mergedOptions.image=utils.resolve(mergedOptions.image)}this.validateOptions(options,"open");if(buttonId){checkout=this.checkouts[buttonId]}else{checkout=new Checkout(options.token,this.host)}this.trackOpen(checkout,mergedOptions);return checkout.open(mergedOptions)};App.prototype.configure=function(buttonId,options){if(options==null){options={}}if(buttonId instanceof Object){options=buttonId;buttonId="button"+this.totalButtons++}if(options.image){options.image=utils.resolve(options.image)}this.validateOptions(options,"configure");this.configurations[buttonId]=options;this.checkouts[buttonId]=new Checkout(options.token,this.host);this.checkouts[buttonId].preload(options);return{open:function(_this){return function(options){return _this.open(options,buttonId)}}(this)}};App.prototype.validateOptions=function(options,which){var url;try{return JSON.stringify(options)}catch(_error){url="https://stripe.com/docs/checkout#integration-custom";throw new Error("Stripe Checkout was unable to serialize the options passed to StripeCheckout."+which+"(). Please consult the doumentation to confirm that you're supplying values of the expected type: "+url)}};App.prototype.setHost=function(host){return this.host=host};App.prototype.trackOpen=function(checkout,options){tracker.setEnabled(!options.notrack);return tracker.track.outerOpen({key:options.key,lsid:"NA",cid:"NA"})};return App}();module.exports=App}).call(this)}});StripeCheckout.require.define({"outer/controllers/button":function(exports,require,module){(function(){var $$,Button,addClass,append,hasAttr,hasClass,helpers,insertAfter,parents,text,trigger,_ref,__bind=function(fn,me){return function(){return fn.apply(me,arguments)}};_ref=require("outer/lib/utils"),$$=_ref.$$,hasClass=_ref.hasClass,addClass=_ref.addClass,trigger=_ref.trigger,append=_ref.append,text=_ref.text,parents=_ref.parents,insertAfter=_ref.insertAfter,hasAttr=_ref.hasAttr;helpers=require("lib/helpers");Button=function(){Button.totalButtonId=0;Button.load=function(app){var button,el,element;element=$$("stripe-button");element=function(){var _i,_len,_results;_results=[];for(_i=0,_len=element.length;_i<_len;_i++){el=element[_i];if(!hasClass(el,"active")){_results.push(el)}}return _results}();element=element[element.length-1];if(!element){return}addClass(element,"active");button=new Button(element,app);return button.append()};function Button(scriptEl,app){this.parseOptions=__bind(this.parseOptions,this);this.parentHead=__bind(this.parentHead,this);this.parentForm=__bind(this.parentForm,this);this.onToken=__bind(this.onToken,this);this.open=__bind(this.open,this);this.submit=__bind(this.submit,this);this.append=__bind(this.append,this);this.render=__bind(this.render,this);var _base;this.scriptEl=scriptEl;this.app=app;this.document=this.scriptEl.ownerDocument;this.nostyle=helpers.isFallback();this.options=this.parseOptions();(_base=this.options).label||(_base.label="Pay with Card");this.options.token=this.onToken;this.$el=document.createElement("button");this.$el.setAttribute("type","submit");this.$el.className="stripe-button-el";helpers.bind(this.$el,"click",this.submit);helpers.bind(this.$el,"touchstart",function(){});this.render()}Button.prototype.render=function(){this.$el.innerHTML="";this.$span=document.createElement("span");text(this.$span,this.options.label);if(!this.nostyle){this.$el.style.visibility="hidden";this.$span.style.display="block";this.$span.style.minHeight="30px"}this.$style=document.createElement("link");this.$style.setAttribute("type","text/css");this.$style.setAttribute("rel","stylesheet");this.$style.setAttribute("href",this.app.host+"/v3/checkout/button.css");return append(this.$el,this.$span)};Button.prototype.append=function(){var head;if(this.scriptEl){insertAfter(this.scriptEl,this.$el)}if(!this.nostyle){head=this.parentHead();if(head){append(head,this.$style)}}if(this.$form=this.parentForm()){helpers.unbind(this.$form,"submit",this.submit);helpers.bind(this.$form,"submit",this.submit)}if(!this.nostyle){setTimeout(function(_this){return function(){return _this.$el.style.visibility="visible"}}(this),1e3)}this.app.setHost(helpers.host(this.scriptEl.src));return this.appHandler=this.app.configure(this.options,{form:this.$form})};Button.prototype.disable=function(){return this.$el.setAttribute("disabled",true)};Button.prototype.enable=function(){return this.$el.removeAttribute("disabled")};Button.prototype.isDisabled=function(){return hasAttr(this.$el,"disabled")};Button.prototype.submit=function(e){if(typeof e.preventDefault==="function"){e.preventDefault()}if(!this.isDisabled()){this.open()}return false};Button.prototype.open=function(){return this.appHandler.open(this.options)};Button.prototype.onToken=function(token,args){var $input,$tokenInput,$tokenTypeInput,key,value;trigger(this.scriptEl,"token",token);if(this.$form){$tokenInput=this.renderInput("stripeToken",token.id);append(this.$form,$tokenInput);$tokenTypeInput=this.renderInput("stripeTokenType",token.type);append(this.$form,$tokenTypeInput);if(token.email){append(this.$form,this.renderInput("stripeEmail",token.email))}if(args){for(key in args){value=args[key];$input=this.renderInput(this.formatKey(key),value);append(this.$form,$input)}}this.$form.submit()}return this.disable()};Button.prototype.formatKey=function(key){var arg,args,_i,_len;args=key.split("_");key="";for(_i=0,_len=args.length;_i<_len;_i++){arg=args[_i];if(arg.length>0){key=key+arg.substr(0,1).toUpperCase()+arg.substr(1).toLowerCase()}}return"stripe"+key};Button.prototype.renderInput=function(name,value){var input;input=document.createElement("input");input.type="hidden";input.name=name;input.value=value;return input};Button.prototype.parentForm=function(){var el,elements,_i,_len,_ref1;elements=parents(this.$el);for(_i=0,_len=elements.length;_i<_len;_i++){el=elements[_i];if(((_ref1=el.tagName)!=null?_ref1.toLowerCase():void 0)==="form"){return el}}return null};Button.prototype.parentHead=function(){var _ref1,_ref2;return((_ref1=this.document)!=null?_ref1.head:void 0)||((_ref2=this.document)!=null?_ref2.getElementsByTagName("head")[0]:void 0)||this.document.body};Button.prototype.parseOptions=function(){var attr,match,options,_i,_len,_ref1;options={};_ref1=this.scriptEl.attributes;for(_i=0,_len=_ref1.length;_i<_len;_i++){attr=_ref1[_i];match=attr.name.match(/^data-(.+)$/);if(match!=null?match[1]:void 0){options[match[1]]=attr.value}}return options};return Button}();module.exports=Button}).call(this)}});StripeCheckout.require.define({"outer/controllers/checkout":function(exports,require,module){(function(){var Checkout,FallbackView,IframeView,TabView,cookie,helpers,__bind=function(fn,me){return function(){return fn.apply(me,arguments)}};helpers=require("lib/helpers");IframeView=require("outer/views/iframeView");TabView=require("outer/views/tabView");FallbackView=require("outer/views/fallbackView");cookie=require("vendor/cookie");Checkout=function(){Checkout.activeView=null;function Checkout(onToken,host){this.preload=__bind(this.preload,this);this.open=__bind(this.open,this);var path,shouldPopup,viewClass;this.onToken=onToken;this.host=host;this.onToken=function(data){return onToken(data.token,data.args)};if(helpers.isFallback()){viewClass=FallbackView;path="/v3/fallback"}else{path="/v3";shouldPopup=helpers.isSupportedMobileOS()&&!(helpers.isiOSWebView()||helpers.isAndroidWebapp()||helpers.isiOSChrome()||helpers.isiPad()&&helpers.iOSVersion()>=8);if(!shouldPopup){viewClass=IframeView}else{viewClass=TabView}}this.view=new viewClass(this.onToken,this.host,path)}Checkout.prototype.open=function(options){if(options==null){options={}}if(Checkout.activeView&&Checkout.activeView!==this.view){Checkout.activeView.close()}Checkout.activeView=this.view;return this.view.open(options,function(_this){return function(status){if(status){return}if(!(_this.view instanceof TabView)){return}_this.view=new IframeView(_this.onToken,_this.host,"/v3");return _this.open(options)}}(this))};Checkout.prototype.preload=function(options){return this.view.preload(options)};return Checkout}();module.exports=Checkout}).call(this)}});StripeCheckout.require.define({"outer/views/fallbackView":function(exports,require,module){(function(){var FallbackRPC,FallbackView,View,__bind=function(fn,me){return function(){return fn.apply(me,arguments)}},__hasProp={}.hasOwnProperty,__extends=function(child,parent){for(var key in parent){if(__hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child};FallbackRPC=require("outer/lib/fallbackRpc");View=require("outer/views/view");FallbackView=function(_super){__extends(FallbackView,_super);function FallbackView(){this.close=__bind(this.close,this);this.open=__bind(this.open,this);FallbackView.__super__.constructor.apply(this,arguments)}FallbackView.prototype.open=function(options,callback){var message,url;FallbackView.__super__.open.apply(this,arguments);url=this.host+this.path;this.frame=window.open(url,"stripe_checkout_app","width=400,height=400,location=yes,resizable=yes,scrollbars=yes");if(this.frame==null){alert("Disable your popup blocker to proceed with checkout.");url="https://stripe.com/docs/checkout#integration-more-runloop";throw new Error("To learn how to prevent the Stripe Checkout popup from being blocked, please visit "+url)}this.rpc=new FallbackRPC(this.frame,url);this.rpc.receiveMessage(function(_this){return function(e){var data;try{data=JSON.parse(e.data)}catch(_error){return}return _this.onToken(data)}}(this));message=JSON.stringify(this.options);this.rpc.invokeTarget(message);return callback(true)};FallbackView.prototype.close=function(){var _ref;return(_ref=this.frame)!=null?_ref.close():void 0};return FallbackView}(View);module.exports=FallbackView}).call(this)}});StripeCheckout.require.define({"outer/views/iframeView":function(exports,require,module){(function(){var IframeView,RPC,View,helpers,ready,utils,__bind=function(fn,me){return function(){return fn.apply(me,arguments)}},__hasProp={}.hasOwnProperty,__extends=function(child,parent){for(var key in parent){if(__hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child};utils=require("outer/lib/utils");helpers=require("lib/helpers");RPC=require("lib/rpc");View=require("outer/views/view");ready=require("vendor/ready");IframeView=function(_super){__extends(IframeView,_super);function IframeView(){this.configure=__bind(this.configure,this);this.removeFrame=__bind(this.removeFrame,this);this.removeTouchOverlay=__bind(this.removeTouchOverlay,this);this.showTouchOverlay=__bind(this.showTouchOverlay,this);this.attachIframe=__bind(this.attachIframe,this);this.setToken=__bind(this.setToken,this);this.closed=__bind(this.closed,this);this.preload=__bind(this.preload,this);this.open=__bind(this.open,this);return IframeView.__super__.constructor.apply(this,arguments)}IframeView.prototype.open=function(options,callback){IframeView.__super__.open.apply(this,arguments);return ready(function(_this){return function(){var left,loaded,_ref;_this.originalOverflowValue=document.body.style.overflow;if(_this.frame==null){_this.configure()}if(typeof $!=="undefined"&&$!==null?(_ref=$.fn)!=null?_ref.modal:void 0:void 0){$(document).off("focusin.bs.modal").off("focusin.modal")}_this.frame.style.display="block";if(_this.shouldShowTouchOverlay()){_this.showTouchOverlay();_this.frame.style.top=(window.scrollY||window.pageYOffset)+"px";left=(window.scrollX||window.pageXOffset)+(window.innerWidth-_this.iframeWidth())/2;left=Math.min(window.innerWidth-_this.iframeWidth(),Math.max(0,left));left=Math.max(0,left);_this.frame.style.left=left+"px"}loaded=false;setTimeout(function(){if(loaded){return}loaded=true;callback(false);return _this.removeFrame()},8e3);return _this.rpc.ready(function(){if(loaded){return}loaded=true;callback(true);_this.rpc.invoke("render","","iframe",_this.options);document.body.style.overflow="hidden";return _this.rpc.invoke("open",{timeLoaded:_this.options.timeLoaded},function(success){var _base;if(success){return typeof(_base=_this.options).opened==="function"?_base.opened():void 0}})})}}(this))};IframeView.prototype.preload=function(options){return ready(function(_this){return function(){_this.configure();return _this.rpc.invoke("preload",options)}}(this))};IframeView.prototype.iframeWidth=function(){if(helpers.isSmallScreen()){return 320}else{return 380}};IframeView.prototype.closed=function(){var _base;document.body.style.overflow=this.originalOverflowValue;this.removeFrame();if(typeof(_base=this.options).closed==="function"){_base.closed()}clearTimeout(this.tokenTimeout);if(this.token!=null){this.onToken(this.token)}this.token=null;this.configure();this.preload(this.options);return true};IframeView.prototype.setToken=function(data){this.token=data;return this.tokenTimeout!=null?this.tokenTimeout:this.tokenTimeout=setTimeout(function(_this){return function(){_this.onToken(_this.token);_this.tokenTimeout=null;return _this.token=null}}(this),2500)};IframeView.prototype.attachIframe=function(){var cssText,iframe;iframe=document.createElement("iframe");iframe.setAttribute("frameBorder","0");iframe.setAttribute("allowtransparency","true");cssText="z-index: 9999;\ndisplay: none;\nbackground: transparent;\nbackground: rgba(0,0,0,0.005);\nborder: 0px none transparent;\noverflow-x: hidden;\noverflow-y: auto;\nvisibility: hidden;\nmargin: 0;\npadding: 0;\n-webkit-tap-highlight-color: transparent;\n-webkit-touch-callout: none;";if(this.shouldShowTouchOverlay()){cssText+="position: absolute;\nwidth: "+this.iframeWidth()+"px;\nheight: 1000px;"}else{cssText+="position: fixed;\nleft: 0;\ntop: 0;\nwidth: 100%;\nheight: 100%;"}iframe.style.cssText=cssText;helpers.bind(iframe,"load",function(){return iframe.style.visibility="visible"});iframe.src=this.host+this.path;iframe.className=iframe.name="stripe_checkout_app";utils.append(document.body,iframe);return iframe};IframeView.prototype.showTouchOverlay=function(){var toRepaint;if(this.overlay){return}this.overlay=document.createElement("div");this.overlay.style.cssText="z-index: 9998;\nbackground: #000;\nopacity: 0;\nborder: 0px none transparent;\noverflow: none;\nmargin: 0;\npadding: 0;\n-webkit-tap-highlight-color: transparent;\n-webkit-touch-callout: none;\nposition: fixed;\nleft: 0;\ntop: 0;\nwidth: 200%;\nheight: 200%;\ntransition: opacity 320ms ease;\n-webkit-transition: opacity 320ms ease;\n-moz-transition: opacity 320ms ease;\n-ms-transition: opacity 320ms ease;";utils.append(document.body,this.overlay);toRepaint=this.overlay.offsetHeight;return this.overlay.style.opacity="0.5"};IframeView.prototype.removeTouchOverlay=function(){var overlay;if(!this.overlay){return}overlay=this.overlay;overlay.style.opacity="0";setTimeout(function(){return utils.remove(overlay)},400);return this.overlay=null};IframeView.prototype.removeFrame=function(){var frame;if(this.shouldShowTouchOverlay()){this.removeTouchOverlay()}frame=this.frame;setTimeout(function(){return utils.remove(frame)},500);return this.frame=null};IframeView.prototype.configure=function(){if(this.frame!=null){this.removeFrame()}this.frame=this.attachIframe();this.rpc=new RPC(this.frame.contentWindow,{host:this.host});this.rpc.methods.closed=this.closed;return this.rpc.methods.setToken=this.setToken};IframeView.prototype.shouldShowTouchOverlay=function(){return helpers.isSupportedMobileOS()};return IframeView}(View);module.exports=IframeView}).call(this)}});StripeCheckout.require.define({"outer/views/tabView":function(exports,require,module){(function(){var RPC,TabView,View,helpers,__bind=function(fn,me){return function(){return fn.apply(me,arguments)}},__hasProp={}.hasOwnProperty,__extends=function(child,parent){for(var key in parent){if(__hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child};RPC=require("lib/rpc");helpers=require("lib/helpers");View=require("outer/views/view");TabView=function(_super){__extends(TabView,_super);function TabView(){this.closed=__bind(this.closed,this);this.checkForClosedTab=__bind(this.checkForClosedTab,this);this.setToken=__bind(this.setToken,this);this.fullPath=__bind(this.fullPath,this);this.close=__bind(this.close,this);this.open=__bind(this.open,this);TabView.__super__.constructor.apply(this,arguments);this.closedTabInterval=null;this.color=null;this.colorSet=false}TabView.prototype.open=function(options,callback){var targetName,url,_base,_base1,_ref,_ref1;TabView.__super__.open.apply(this,arguments);if((_ref=this.frame)!=null?_ref.opener:void 0){if(typeof(_base=this.frame).focus==="function"){_base.focus()}return}if(window.name==="stripe_checkout_tabview"){window.name=""}if(helpers.isiOSChrome()){targetName="_blank"}else{targetName="stripe_checkout_tabview"}this.frame=window.open(this.fullPath(),targetName);if(!this.frame&&((_ref1=this.options.key)!=null?_ref1.indexOf("test"):void 0)!==-1){url="https://stripe.com/docs/checkout#integration-more-runloop";console.error("Stripe Checkout was unable to open a new window, possibly due to a popup blocker.\nTo provide the best experience for your users, follow the guide at "+url+".\nThis message will only appear when using a test publishable key.")}if(!this.frame||this.frame===window){this.close();callback(false);return}if(typeof(_base1=this.frame).focus==="function"){_base1.focus()}this.rpc=new RPC(this.frame,{host:this.host});this.rpc.methods.setToken=this.setToken;this.rpc.methods.closed=this.closed;return this.rpc.ready(function(_this){return function(){var _base2;callback(true);_this.rpc.invoke("render","","tab",_this.options);_this.rpc.invoke("open");if(typeof(_base2=_this.options).opened==="function"){_base2.opened()}return _this.checkForClosedTab()}}(this))};TabView.prototype.close=function(){if(this.frame&&this.frame!==window){return this.frame.close()}};TabView.prototype.fullPath=function(){return this.host+this.path};TabView.prototype.setToken=function(data){this.token=data;return this.tokenTimeout!=null?this.tokenTimeout:this.tokenTimeout=setTimeout(function(_this){return function(){_this.onToken(_this.token);_this.tokenTimeout=null;return _this.token=null}}(this),2500)};TabView.prototype.checkForClosedTab=function(){if(this.closedTabInterval){clearInterval(this.closedTabInterval)}return this.closedTabInterval=setInterval(function(_this){return function(){if(!_this.frame||!_this.frame.postMessage||_this.frame.closed){return _this.closed()}}}(this),100)};TabView.prototype.closed=function(){var _base;clearInterval(this.closedTabInterval);if(typeof(_base=this.options).closed==="function"){_base.closed()}clearTimeout(this.tokenTimeout);if(this.token!=null){return this.onToken(this.token)}};return TabView}(View);module.exports=TabView}).call(this)}});StripeCheckout.require.define({"outer/views/view":function(exports,require,module){(function(){var View,__bind=function(fn,me){return function(){return fn.apply(me,arguments)}};View=function(){function View(onToken,host,path){this.open=__bind(this.open,this);this.onToken=onToken;this.host=host;this.path=path}View.prototype.open=function(options,callback){return this.options=options};View.prototype.close=function(){};View.prototype.preload=function(options){};return View}();module.exports=View}).call(this)}});(function(){var App,Button,app,require,_ref,_ref1;require=require||this.StripeCheckout.require;Button=require("outer/controllers/button");App=require("outer/controllers/app");if(((_ref=this.StripeCheckout)!=null?_ref.__app:void 0)==null){this.StripeCheckout||(this.StripeCheckout={});this.StripeCheckout.__app=app=new App;this.StripeCheckout.open=app.open;this.StripeCheckout.configure=app.configure;this.StripeButton=this.StripeCheckout;if(((_ref1=this.StripeCheckout)!=null?_ref1.__host:void 0)&&this.StripeCheckout.__host!==""){app.setHost(this.StripeCheckout.__host)}}Button.load(this.StripeCheckout.__app)}).call(this); -//# sourceMappingURL=https://sourcemaps.stripe.com/checkout/checkout.js.map \ No newline at end of file diff --git a/vendor/scripts/coffeescript-1.6.3.js b/vendor/scripts/coffeescript.js similarity index 100% rename from vendor/scripts/coffeescript-1.6.3.js rename to vendor/scripts/coffeescript.js diff --git a/vendor/scripts/jasmine-boot.js b/vendor/scripts/jasmine-boot.js new file mode 100755 index 000000000..47b125211 --- /dev/null +++ b/vendor/scripts/jasmine-boot.js @@ -0,0 +1,176 @@ +/** + Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. + + If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms. + + The location of `boot.js` can be specified and/or overridden in `jasmine.yml`. + + [jasmine-gem]: http://github.com/pivotal/jasmine-gem + */ + +(function() { + + /** + * ## Require & Instantiate + * + * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. + */ + window.jasmine = jasmineRequire.core(jasmineRequire); + + /** + * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. + */ + jasmineRequire.html(jasmine); + + /** + * Create the Jasmine environment. This is used to run all specs in a project. + */ + var env = jasmine.getEnv(); + + /** + * ## The Global Interface + * + * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. + */ + var jasmineInterface = { + describe: function(description, specDefinitions) { + return env.describe(description, specDefinitions); + }, + + xdescribe: function(description, specDefinitions) { + return env.xdescribe(description, specDefinitions); + }, + + it: function(desc, func) { + return env.it(desc, func); + }, + + xit: function(desc, func) { + return env.xit(desc, func); + }, + + beforeEach: function(beforeEachFunction) { + return env.beforeEach(beforeEachFunction); + }, + + afterEach: function(afterEachFunction) { + return env.afterEach(afterEachFunction); + }, + + expect: function(actual) { + return env.expect(actual); + }, + + pending: function() { + return env.pending(); + }, + + spyOn: function(obj, methodName) { + return env.spyOn(obj, methodName); + }, + + jsApiReporter: new jasmine.JsApiReporter({ + timer: new jasmine.Timer() + }) + }; + + /** + * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. + */ + if (typeof window == "undefined" && typeof exports == "object") { + extend(exports, jasmineInterface); + } else { + extend(window, jasmineInterface); + } + + /** + * Expose the interface for adding custom equality testers. + */ + jasmine.addCustomEqualityTester = function(tester) { + env.addCustomEqualityTester(tester); + }; + + /** + * Expose the interface for adding custom expectation matchers + */ + jasmine.addMatchers = function(matchers) { + return env.addMatchers(matchers); + }; + + /** + * Expose the mock interface for the JavaScript timeout functions + */ + jasmine.clock = function() { + return env.clock; + }; + + /** + * ## Runner Parameters + * + * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. + */ + + var queryString = new jasmine.QueryString({ + getWindowLocation: function() { return window.location; } + }); + + var catchingExceptions = false; // Setting to false always for CodeCombat, because Jasmine-HTML's reporter doesn't do source maps, and Chrome console does. + env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); + + /** + * ## Reporters + * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). + */ + var htmlReporter = new jasmine.HtmlReporter({ + env: env, + onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); }, + getContainer: function() { return $('#testing-area')[0]; }, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); }, + timer: new jasmine.Timer() + }); + + /** + * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. + */ + env.addReporter(jasmineInterface.jsApiReporter); + env.addReporter(htmlReporter); + + /** + * Filter which specs will be run by matching the start of the full name against the `spec` query param. + */ + var specFilter = new jasmine.HtmlSpecFilter({ + filterString: function() { return queryString.getParam("spec"); } + }); + + env.specFilter = function(spec) { + return specFilter.matches(spec.getFullName()); + }; + + /** + * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. + */ + window.setTimeout = window.setTimeout; + window.setInterval = window.setInterval; + window.clearTimeout = window.clearTimeout; + window.clearInterval = window.clearInterval; + + /** + * ## Execution + * + * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. + */ + window.runJasmine = function() { + htmlReporter.initialize(); + env.execute(); + }; + + /** + * Helper function for readability above. + */ + function extend(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; + } + +}()); diff --git a/app/assets/javascripts/jasmine-html.js b/vendor/scripts/jasmine-html.js similarity index 100% rename from app/assets/javascripts/jasmine-html.js rename to vendor/scripts/jasmine-html.js diff --git a/vendor/scripts/jasmine-mock-ajax.js b/vendor/scripts/jasmine-mock-ajax.js new file mode 100644 index 000000000..6e4402fdf --- /dev/null +++ b/vendor/scripts/jasmine-mock-ajax.js @@ -0,0 +1,609 @@ +/* + +Jasmine-Ajax : a set of helpers for testing AJAX requests under the Jasmine +BDD framework for JavaScript. + +http://github.com/pivotal/jasmine-ajax + +Jasmine Home page: http://pivotal.github.com/jasmine + +Copyright (c) 2008-2013 Pivotal Labs + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +getJasmineRequireObj().ajax = function(jRequire) { + var $ajax = {}; + + $ajax.RequestStub = jRequire.AjaxRequestStub(); + $ajax.RequestTracker = jRequire.AjaxRequestTracker(); + $ajax.StubTracker = jRequire.AjaxStubTracker(); + $ajax.ParamParser = jRequire.AjaxParamParser(); + $ajax.fakeRequest = jRequire.AjaxFakeRequest(); + $ajax.MockAjax = jRequire.MockAjax($ajax); + + return $ajax.MockAjax; +}; + +getJasmineRequireObj().AjaxFakeRequest = function() { + function extend(destination, source, propertiesToSkip) { + propertiesToSkip = propertiesToSkip || []; + for (var property in source) { + if (!arrayContains(propertiesToSkip, property)) { + destination[property] = source[property]; + } + } + return destination; + } + + function arrayContains(arr, item) { + for (var i = 0; i < arr.length; i++) { + if (arr[i] === item) { + return true; + } + } + return false; + } + + function wrapProgressEvent(xhr, eventName) { + return function() { + if (xhr[eventName]) { + xhr[eventName](); + } + }; + } + + function initializeEvents(xhr) { + return { + 'loadstart': wrapProgressEvent(xhr, 'onloadstart'), + 'load': wrapProgressEvent(xhr, 'onload'), + 'loadend': wrapProgressEvent(xhr, 'onloadend'), + 'progress': wrapProgressEvent(xhr, 'onprogress'), + 'error': wrapProgressEvent(xhr, 'onerror'), + 'abort': wrapProgressEvent(xhr, 'onabort'), + 'timeout': wrapProgressEvent(xhr, 'ontimeout') + }; + } + + function unconvertibleResponseTypeMessage(type) { + var msg = [ + "Can't build XHR.response for XHR.responseType of '", + type, + "'.", + "XHR.response must be explicitly stubbed" + ]; + return msg.join(' '); + } + + function fakeRequest(global, requestTracker, stubTracker, paramParser) { + function FakeXMLHttpRequest() { + requestTracker.track(this); + this.events = initializeEvents(this); + this.requestHeaders = {}; + this.overriddenMimeType = null; + } + + function findHeader(name, headers) { + name = name.toLowerCase(); + for (var header in headers) { + if (header.toLowerCase() === name) { + return headers[header]; + } + } + } + + function normalizeHeaders(rawHeaders, contentType) { + var headers = []; + + if (rawHeaders) { + if (rawHeaders instanceof Array) { + headers = rawHeaders; + } else { + for (var headerName in rawHeaders) { + if (rawHeaders.hasOwnProperty(headerName)) { + headers.push({ name: headerName, value: rawHeaders[headerName] }); + } + } + } + } else { + headers.push({ name: "Content-Type", value: contentType || "application/json" }); + } + + return headers; + } + + function parseXml(xmlText, contentType) { + if (global.DOMParser) { + return (new global.DOMParser()).parseFromString(xmlText, 'text/xml'); + } else { + var xml = new global.ActiveXObject("Microsoft.XMLDOM"); + xml.async = "false"; + xml.loadXML(xmlText); + return xml; + } + } + + var xmlParsables = ['text/xml', 'application/xml']; + + function getResponseXml(responseText, contentType) { + if (arrayContains(xmlParsables, contentType.toLowerCase())) { + return parseXml(responseText, contentType); + } else if (contentType.match(/\+xml$/)) { + return parseXml(responseText, 'text/xml'); + } + return null; + } + + var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout']; + extend(FakeXMLHttpRequest.prototype, new global.XMLHttpRequest(), iePropertiesThatCannotBeCopied); + extend(FakeXMLHttpRequest.prototype, { + open: function() { + this.method = arguments[0]; + this.url = arguments[1]; + this.username = arguments[3]; + this.password = arguments[4]; + this.readyState = 1; + this.onreadystatechange(); + }, + + setRequestHeader: function(header, value) { + if(this.requestHeaders.hasOwnProperty(header)) { + this.requestHeaders[header] = [this.requestHeaders[header], value].join(', '); + } else { + this.requestHeaders[header] = value; + } + }, + + overrideMimeType: function(mime) { + this.overriddenMimeType = mime; + }, + + abort: function() { + this.readyState = 0; + this.status = 0; + this.statusText = "abort"; + this.onreadystatechange(); + this.events.progress(); + this.events.abort(); + this.events.loadend(); + }, + + readyState: 0, + + onloadstart: null, + onprogress: null, + onabort: null, + onerror: null, + onload: null, + ontimeout: null, + onloadend: null, + + onreadystatechange: function(isTimeout) { + }, + + addEventListener: function(event, callback) { + var existingCallback = this.events[event], + self = this; + + this.events[event] = function() { + callback.apply(self); + existingCallback(); + }; + }, + + status: null, + + send: function(data) { + this.params = data; + this.readyState = 2; + this.events.loadstart(); + this.onreadystatechange(); + + var stub = stubTracker.findStub(this.url, data, this.method); + if (stub) { + this.respondWith(stub); + } + }, + + contentType: function() { + return findHeader('content-type', this.requestHeaders); + }, + + data: function() { + if (!this.params) { + return {}; + } + + return paramParser.findParser(this).parse(this.params); + }, + + getResponseHeader: function(name) { + name = name.toLowerCase(); + var resultHeader; + for(var i = 0; i < this.responseHeaders.length; i++) { + var header = this.responseHeaders[i]; + if (name === header.name.toLowerCase()) { + if (resultHeader) { + resultHeader = [resultHeader, header.value].join(', '); + } else { + resultHeader = header.value; + } + } + } + return resultHeader; + }, + + getAllResponseHeaders: function() { + var responseHeaders = []; + for (var i = 0; i < this.responseHeaders.length; i++) { + responseHeaders.push(this.responseHeaders[i].name + ': ' + + this.responseHeaders[i].value); + } + return responseHeaders.join('\r\n'); + }, + + responseText: null, + response: null, + responseType: '', + + responseValue: function() { + switch(this.responseType) { + case null: + case "": + case "text": + return this.readyState >= 3 ? this.responseText : ""; + case "json": + return JSON.parse(this.responseText); + case "arraybuffer": + throw unconvertibleResponseTypeMessage('arraybuffer'); + case "blob": + throw unconvertibleResponseTypeMessage('blob'); + case "document": + return this.responseXML; + } + }, + + + respondWith: function(response) { + if (this.readyState === 4) { + throw new Error("FakeXMLHttpRequest already completed"); + } + this.status = response.status; + this.statusText = response.statusText || ""; + this.responseText = response.responseText || ""; + this.responseType = response.responseType || ""; + this.readyState = 4; + this.responseHeaders = normalizeHeaders(response.responseHeaders, response.contentType); + this.responseXML = getResponseXml(response.responseText, this.getResponseHeader('content-type') || ''); + if (this.responseXML) { + this.responseType = 'document'; + } + + if ('response' in response) { + this.response = response.response; + } else { + this.response = this.responseValue(); + } + + this.onreadystatechange(); + this.events.progress(); + this.events.load(); + this.events.loadend(); + }, + + responseTimeout: function() { + if (this.readyState === 4) { + throw new Error("FakeXMLHttpRequest already completed"); + } + this.readyState = 4; + jasmine.clock().tick(30000); + this.onreadystatechange('timeout'); + this.events.progress(); + this.events.timeout(); + this.events.loadend(); + }, + + responseError: function() { + if (this.readyState === 4) { + throw new Error("FakeXMLHttpRequest already completed"); + } + this.readyState = 4; + this.onreadystatechange(); + this.events.progress(); + this.events.error(); + this.events.loadend(); + } + }); + + return FakeXMLHttpRequest; + } + + return fakeRequest; +}; + +getJasmineRequireObj().MockAjax = function($ajax) { + function MockAjax(global) { + var requestTracker = new $ajax.RequestTracker(), + stubTracker = new $ajax.StubTracker(), + paramParser = new $ajax.ParamParser(), + realAjaxFunction = global.XMLHttpRequest, + mockAjaxFunction = $ajax.fakeRequest(global, requestTracker, stubTracker, paramParser); + + this.install = function() { + global.XMLHttpRequest = mockAjaxFunction; + }; + + this.uninstall = function() { + global.XMLHttpRequest = realAjaxFunction; + + this.stubs.reset(); + this.requests.reset(); + paramParser.reset(); + }; + + this.stubRequest = function(url, data, method) { + var stub = new $ajax.RequestStub(url, data, method); + stubTracker.addStub(stub); + return stub; + }; + + this.withMock = function(closure) { + this.install(); + try { + closure(); + } finally { + this.uninstall(); + } + }; + + this.addCustomParamParser = function(parser) { + paramParser.add(parser); + }; + + this.requests = requestTracker; + this.stubs = stubTracker; + } + + return MockAjax; +}; + +getJasmineRequireObj().AjaxParamParser = function() { + function ParamParser() { + var defaults = [ + { + test: function(xhr) { + return (/^application\/json/).test(xhr.contentType()); + }, + parse: function jsonParser(paramString) { + return JSON.parse(paramString); + } + }, + { + test: function(xhr) { + return true; + }, + parse: function naiveParser(paramString) { + var data = {}; + var params = paramString.split('&'); + + for (var i = 0; i < params.length; ++i) { + var kv = params[i].replace(/\+/g, ' ').split('='); + var key = decodeURIComponent(kv[0]); + data[key] = data[key] || []; + data[key].push(decodeURIComponent(kv[1])); + } + return data; + } + } + ]; + var paramParsers = []; + + this.add = function(parser) { + paramParsers.unshift(parser); + }; + + this.findParser = function(xhr) { + for(var i in paramParsers) { + var parser = paramParsers[i]; + if (parser.test(xhr)) { + return parser; + } + } + }; + + this.reset = function() { + paramParsers = []; + for(var i in defaults) { + paramParsers.push(defaults[i]); + } + }; + + this.reset(); + } + + return ParamParser; +}; + +getJasmineRequireObj().AjaxRequestStub = function() { + function RequestStub(url, stubData, method) { + var normalizeQuery = function(query) { + return query ? query.split('&').sort().join('&') : undefined; + }; + + if (url instanceof RegExp) { + this.url = url; + this.query = undefined; + } else { + var split = url.split('?'); + this.url = split[0]; + this.query = split.length > 1 ? normalizeQuery(split[1]) : undefined; + } + + this.data = normalizeQuery(stubData); + this.method = method; + + this.andReturn = function(options) { + this.status = options.status || 200; + + this.contentType = options.contentType; + this.response = options.response; + this.responseText = options.responseText; + }; + + this.matches = function(fullUrl, data, method) { + var matches = false; + fullUrl = fullUrl.toString(); + if (this.url instanceof RegExp) { + matches = this.url.test(fullUrl); + } else { + var urlSplit = fullUrl.split('?'), + url = urlSplit[0], + query = urlSplit[1]; + matches = this.url === url && this.query === normalizeQuery(query); + } + return matches && (!this.data || this.data === normalizeQuery(data)) && (!this.method || this.method === method); + }; + } + + return RequestStub; +}; + +getJasmineRequireObj().AjaxRequestTracker = function() { + function RequestTracker() { + var requests = []; + + this.track = function(request) { + requests.push(request); + }; + + this.first = function() { + return requests[0]; + }; + + this.count = function() { + return requests.length; + }; + + this.reset = function() { + requests = []; + }; + + this.mostRecent = function() { + return requests[requests.length - 1]; + }; + + this.at = function(index) { + return requests[index]; + }; + + this.all = function() { + return requests.slice(0); + }; + + this.sendResponses = function(responseMap) { + var urls = Object.keys(responseMap); + var success = true; + for(var i in urls) { + var url = urls[i]; + var responseBody = responseMap[url]; + var responded = false; + + var requests = jasmine.Ajax.requests.all().slice(); + for(var j in requests) { + var request = requests[j]; + if(_.string.startsWith(request.url, url)) { + request.respondWith({status: 200, responseText: JSON.stringify(responseBody)}); + responded = true; + break; + } + } + if(!responded) { + var allRequests = jasmine.Ajax.requests.all(); + urls = []; + for(var k in allRequests) urls.push(allRequests[k].url); + console.error('could not find response for', url, 'in', urls, allRequests); + success = false; + continue; + } + } + return success; + } + + this.filter = function(url_to_match) { + var matching_requests = []; + + for (var i = 0; i < requests.length; i++) { + if (url_to_match instanceof RegExp && + url_to_match.test(requests[i].url)) { + matching_requests.push(requests[i]); + } else if (url_to_match instanceof Function && + url_to_match(requests[i])) { + matching_requests.push(requests[i]); + } else { + if (requests[i].url === url_to_match) { + matching_requests.push(requests[i]); + } + } + } + + return matching_requests; + }; + } + + return RequestTracker; +}; + +getJasmineRequireObj().AjaxStubTracker = function() { + function StubTracker() { + var stubs = []; + + this.addStub = function(stub) { + stubs.push(stub); + }; + + this.reset = function() { + stubs = []; + }; + + this.findStub = function(url, data, method) { + for (var i = stubs.length - 1; i >= 0; i--) { + var stub = stubs[i]; + if (stub.matches(url, data, method)) { + return stub; + } + } + }; + } + + return StubTracker; +}; + +(function() { + var jRequire = getJasmineRequireObj(), + MockAjax = jRequire.ajax(jRequire); + if (typeof window === "undefined" && typeof exports === "object") { + exports.MockAjax = MockAjax; + jasmine.Ajax = new MockAjax(exports); + } else { + window.MockAjax = MockAjax; + jasmine.Ajax = new MockAjax(window); + } +}()); diff --git a/app/assets/javascripts/jasmine.js b/vendor/scripts/jasmine.js similarity index 100% rename from app/assets/javascripts/jasmine.js rename to vendor/scripts/jasmine.js diff --git a/vendor/scripts/soundjs-NEXT.combined.js b/vendor/scripts/soundjs-NEXT.combined.js index 346983e77..b2762133a 100644 --- a/vendor/scripts/soundjs-NEXT.combined.js +++ b/vendor/scripts/soundjs-NEXT.combined.js @@ -1,6 +1,9 @@ -/** - * @module SoundJS - */ + + +//############################################################################## +// version.js +//############################################################################## + this.createjs = this.createjs || {}; (function () { @@ -19,7 +22,7 @@ this.createjs = this.createjs || {}; * @type String * @static **/ - s.version = /*version*/"NEXT"; // injected by build process + s.version = /*=version*/""; // injected by build process /** * The build date for this release in UTC format. @@ -27,103 +30,366 @@ this.createjs = this.createjs || {}; * @type String * @static **/ - s.buildDate = /*date*/"Mon, 27 Oct 2014 20:40:07 GMT"; // injected by build process + s.buildDate = /*=date*/""; // injected by build process })(); -/* -* EventDispatcher -* Visit http://createjs.com/ for documentation, updates and examples. -* -* Copyright (c) 2010 gskinner.com, inc. -* -* Permission is hereby granted, free of charge, to any person -* obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without -* restriction, including without limitation the rights to use, -* copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following -* conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -*/ + +//############################################################################## +// extend.js +//############################################################################## + +this.createjs = this.createjs||{}; /** - * @module CreateJS + * @class Utility Methods */ -// namespace: +/** + * Sets up the prototype chain and constructor property for a new class. + * + * This should be called right after creating the class constructor. + * + * function MySubClass() {} + * createjs.extend(MySubClass, MySuperClass); + * ClassB.prototype.doSomething = function() { } + * + * var foo = new MySubClass(); + * console.log(foo instanceof MySuperClass); // true + * console.log(foo.prototype.constructor === MySubClass); // true + * + * @method extends + * @param {Function} subclass The subclass. + * @param {Function} superclass The superclass to extend. + * @return {Function} Returns the subclass's new prototype. + */ +createjs.extend = function(subclass, superclass) { + "use strict"; + + function o() { this.constructor = subclass; } + o.prototype = superclass.prototype; + return (subclass.prototype = new o()); +}; + +//############################################################################## +// promote.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/** + * @class Utility Methods + */ + +/** + * Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`. + * It is recommended to use the super class's name as the prefix. + * An alias to the super class's constructor is always added in the format `prefix_constructor`. + * This allows the subclass to call super class methods without using `function.call`, providing better performance. + * + * For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")` + * would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the + * prototype of `MySubClass` as `MySuperClass_draw`. + * + * This should be called after the class's prototype is fully defined. + * + * function ClassA(name) { + * this.name = name; + * } + * ClassA.prototype.greet = function() { + * return "Hello "+this.name; + * } + * + * function ClassB(name, punctuation) { + * this.ClassA_constructor(name); + * this.punctuation = punctuation; + * } + * createjs.extend(ClassB, ClassA); + * ClassB.prototype.greet = function() { + * return this.ClassA_greet()+this.punctuation; + * } + * createjs.promote(ClassB, "ClassA"); + * + * var foo = new ClassB("World", "!?!"); + * console.log(foo.greet()); // Hello World!?! + * + * @method promote + * @param {Function} subclass The class to promote super class methods on. + * @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass. + * @return {Function} Returns the subclass. + */ +createjs.promote = function(subclass, prefix) { + "use strict"; + + var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__; + if (supP) { + subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable + for (var n in supP) { + if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; } + } + } + return subclass; +}; + +//############################################################################## +// IndexOf.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/** + * @class Utility Methods + */ + +/** + * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of + * that value. Returns -1 if value is not found. + * + * var i = createjs.indexOf(myArray, myElementToFind); + * + * @method indexOf + * @param {Array} array Array to search for searchElement + * @param searchElement Element to find in array. + * @return {Number} The first index of searchElement in array. + */ +createjs.indexOf = function (array, searchElement){ + "use strict"; + + for (var i = 0,l=array.length; i < l; i++) { + if (searchElement === array[i]) { + return i; + } + } + return -1; +}; + +//############################################################################## +// Proxy.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/** + * Various utilities that the CreateJS Suite uses. Utilities are created as separate files, and will be available on the + * createjs namespace directly. + * + * <h4>Example</h4> + * + * myObject.addEventListener("change", createjs.proxy(myMethod, scope)); + * + * @class Utility Methods + * @main Utility Methods + */ + +(function() { + "use strict"; + + /** + * A function proxy for methods. By default, JavaScript methods do not maintain scope, so passing a method as a + * callback will result in the method getting called in the scope of the caller. Using a proxy ensures that the + * method gets called in the correct scope. + * + * Additional arguments can be passed that will be applied to the function when it is called. + * + * <h4>Example</h4> + * + * myObject.addEventListener("event", createjs.proxy(myHandler, this, arg1, arg2)); + * + * function myHandler(arg1, arg2) { + * // This gets called when myObject.myCallback is executed. + * } + * + * @method proxy + * @param {Function} method The function to call + * @param {Object} scope The scope to call the method name on + * @param {mixed} [arg] * Arguments that are appended to the callback for additional params. + * @public + * @static + */ + createjs.proxy = function (method, scope) { + var aArgs = Array.prototype.slice.call(arguments, 2); + return function () { + return method.apply(scope, Array.prototype.slice.call(arguments, 0).concat(aArgs)); + }; + } + +}()); + +//############################################################################## +// definePropertySupported.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/** + * @class Utility Methods + */ +(function() { + "use strict"; + + /** + * Boolean value indicating if Object.defineProperty is supported. + * + * <h4>Example</h4> + * + * if (createjs.definePropertySupported) { // add getter / setter} + * + * @property definePropertySupported + * @type {Boolean} + * @default true + */ + var t = Object.defineProperty ? true : false; + + // IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors + var foo = {}; + try { + Object.defineProperty(foo, "bar", { + get: function () { + return this._bar; + }, + set: function (value) { + this._bar = value; + } + }); + } catch (e) { + t = false; + } + + createjs.definePropertySupported = t; +}()); + +//############################################################################## +// BrowserDetect.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/** + * @class Utility Methods + */ +(function() { + "use strict"; + + /** + * An object that determines the current browser, version, operating system, and other environment + * variables via user agent string. + * + * Used for audio because feature detection is unable to detect the many limitations of mobile devices. + * + * <h4>Example</h4> + * + * if (createjs.BrowserDetect.isIOS) { // do stuff } + * + * @property BrowserDetect + * @type {Object} + * @param {Boolean} isFirefox True if our browser is Firefox. + * @param {Boolean} isOpera True if our browser is opera. + * @param {Boolean} isChrome True if our browser is Chrome. Note that Chrome for Android returns true, but is a + * completely different browser with different abilities. + * @param {Boolean} isIOS True if our browser is safari for iOS devices (iPad, iPhone, and iPod). + * @param {Boolean} isAndroid True if our browser is Android. + * @param {Boolean} isBlackberry True if our browser is Blackberry. + * @constructor + * @static + */ + function BrowserDetect() { + throw "BrowserDetect cannot be instantiated"; + }; + + var agent = BrowserDetect.agent = window.navigator.userAgent; + BrowserDetect.isWindowPhone = (agent.indexOf("IEMobile") > -1) || (agent.indexOf("Windows Phone") > -1); + BrowserDetect.isFirefox = (agent.indexOf("Firefox") > -1); + BrowserDetect.isOpera = (window.opera != null); + BrowserDetect.isChrome = (agent.indexOf("Chrome") > -1); // NOTE that Chrome on Android returns true but is a completely different browser with different abilities + BrowserDetect.isIOS = (agent.indexOf("iPod") > -1 || agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) && !BrowserDetect.isWindowPhone; + BrowserDetect.isAndroid = (agent.indexOf("Android") > -1) && !BrowserDetect.isWindowPhone; + BrowserDetect.isBlackberry = (agent.indexOf("Blackberry") > -1); + + createjs.BrowserDetect = BrowserDetect; + +}()); + +//############################################################################## +// EventDispatcher.js +//############################################################################## + this.createjs = this.createjs||{}; (function() { "use strict"; -/** - * EventDispatcher provides methods for managing queues of event listeners and dispatching events. - * - * You can either extend EventDispatcher or mix its methods into an existing prototype or instance by using the - * EventDispatcher {{#crossLink "EventDispatcher/initialize"}}{{/crossLink}} method. - * - * Together with the CreateJS Event class, EventDispatcher provides an extended event model that is based on the - * DOM Level 2 event model, including addEventListener, removeEventListener, and dispatchEvent. It supports - * bubbling / capture, preventDefault, stopPropagation, stopImmediatePropagation, and handleEvent. - * - * EventDispatcher also exposes a {{#crossLink "EventDispatcher/on"}}{{/crossLink}} method, which makes it easier - * to create scoped listeners, listeners that only run once, and listeners with associated arbitrary data. The - * {{#crossLink "EventDispatcher/off"}}{{/crossLink}} method is merely an alias to - * {{#crossLink "EventDispatcher/removeEventListener"}}{{/crossLink}}. - * - * Another addition to the DOM Level 2 model is the {{#crossLink "EventDispatcher/removeAllEventListeners"}}{{/crossLink}} - * method, which can be used to listeners for all events, or listeners for a specific event. The Event object also - * includes a {{#crossLink "Event/remove"}}{{/crossLink}} method which removes the active listener. - * - * <h4>Example</h4> - * Add EventDispatcher capabilities to the "MyClass" class. - * - * EventDispatcher.initialize(MyClass.prototype); - * - * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}). - * - * instance.addEventListener("eventName", handlerMethod); - * function handlerMethod(event) { - * console.log(event.target + " Was Clicked"); - * } - * - * <b>Maintaining proper scope</b><br /> - * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}} - * method to subscribe to events simplifies this. - * - * instance.addEventListener("click", function(event) { - * console.log(instance == this); // false, scope is ambiguous. - * }); - * - * instance.on("click", function(event) { - * console.log(instance == this); // true, "on" uses dispatcher scope by default. - * }); - * - * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage scope. - * - * - * @class EventDispatcher - * @constructor - **/ -var EventDispatcher = function() { -/* this.initialize(); */ // not needed. -}; -var p = EventDispatcher.prototype; -EventDispatcher.prototype.constructor = EventDispatcher; + +// constructor: + /** + * EventDispatcher provides methods for managing queues of event listeners and dispatching events. + * + * You can either extend EventDispatcher or mix its methods into an existing prototype or instance by using the + * EventDispatcher {{#crossLink "EventDispatcher/initialize"}}{{/crossLink}} method. + * + * Together with the CreateJS Event class, EventDispatcher provides an extended event model that is based on the + * DOM Level 2 event model, including addEventListener, removeEventListener, and dispatchEvent. It supports + * bubbling / capture, preventDefault, stopPropagation, stopImmediatePropagation, and handleEvent. + * + * EventDispatcher also exposes a {{#crossLink "EventDispatcher/on"}}{{/crossLink}} method, which makes it easier + * to create scoped listeners, listeners that only run once, and listeners with associated arbitrary data. The + * {{#crossLink "EventDispatcher/off"}}{{/crossLink}} method is merely an alias to + * {{#crossLink "EventDispatcher/removeEventListener"}}{{/crossLink}}. + * + * Another addition to the DOM Level 2 model is the {{#crossLink "EventDispatcher/removeAllEventListeners"}}{{/crossLink}} + * method, which can be used to listeners for all events, or listeners for a specific event. The Event object also + * includes a {{#crossLink "Event/remove"}}{{/crossLink}} method which removes the active listener. + * + * <h4>Example</h4> + * Add EventDispatcher capabilities to the "MyClass" class. + * + * EventDispatcher.initialize(MyClass.prototype); + * + * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}). + * + * instance.addEventListener("eventName", handlerMethod); + * function handlerMethod(event) { + * console.log(event.target + " Was Clicked"); + * } + * + * <b>Maintaining proper scope</b><br /> + * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}} + * method to subscribe to events simplifies this. + * + * instance.addEventListener("click", function(event) { + * console.log(instance == this); // false, scope is ambiguous. + * }); + * + * instance.on("click", function(event) { + * console.log(instance == this); // true, "on" uses dispatcher scope by default. + * }); + * + * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage scope. + * + * + * @class EventDispatcher + * @constructor + **/ + function EventDispatcher() { + + + // private properties: + /** + * @protected + * @property _listeners + * @type Object + **/ + this._listeners = null; + + /** + * @protected + * @property _captureListeners + * @type Object + **/ + this._captureListeners = null; + } + var p = EventDispatcher.prototype; +// static public methods: /** * Static initializer to mix EventDispatcher methods into a target object or prototype. * @@ -146,30 +412,6 @@ EventDispatcher.prototype.constructor = EventDispatcher; target.willTrigger = p.willTrigger; }; -// constructor: - -// private properties: - /** - * @protected - * @property _listeners - * @type Object - **/ - p._listeners = null; - - /** - * @protected - * @property _captureListeners - * @type Object - **/ - p._captureListeners = null; - -// constructor: - /** - * Initialization method. - * @method initialize - * @protected - **/ - p.initialize = function() {}; // public methods: /** @@ -398,6 +640,7 @@ EventDispatcher.prototype.constructor = EventDispatcher; return "[EventDispatcher]"; }; + // private methods: /** * @method _dispatchEvent @@ -427,209 +670,154 @@ EventDispatcher.prototype.constructor = EventDispatcher; }; -createjs.EventDispatcher = EventDispatcher; + createjs.EventDispatcher = EventDispatcher; }()); -/* -* Event -* Visit http://createjs.com/ for documentation, updates and examples. -* -* Copyright (c) 2010 gskinner.com, inc. -* -* Permission is hereby granted, free of charge, to any person -* obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without -* restriction, including without limitation the rights to use, -* copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following -* conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -*/ -/** - * A collection of Classes that are shared across all the CreateJS libraries. The classes are included in the minified - * files of each library and are available on the createsjs namespace directly. - * - * <h4>Example</h4> - * - * myObject.addEventListener("change", createjs.proxy(myMethod, scope)); - * - * @module CreateJS - * @main CreateJS - */ +//############################################################################## +// Event.js +//############################################################################## -// namespace: this.createjs = this.createjs||{}; (function() { "use strict"; -/** - * Contains properties and methods shared by all events for use with - * {{#crossLink "EventDispatcher"}}{{/crossLink}}. - * - * Note that Event objects are often reused, so you should never - * rely on an event object's state outside of the call stack it was received in. - * @class Event - * @param {String} type The event type. - * @param {Boolean} bubbles Indicates whether the event will bubble through the display list. - * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled. - * @constructor - **/ -var Event = function(type, bubbles, cancelable) { - this.initialize(type, bubbles, cancelable); -}; -var p = Event.prototype; -Event.prototype.constructor = Event; - -// events: - -// public properties: - - /** - * The type of event. - * @property type - * @type String - **/ - p.type = null; - - /** - * The object that generated an event. - * @property target - * @type Object - * @default null - * @readonly - */ - p.target = null; - - /** - * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will - * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event - * is generated from childObj, then a listener on parentObj would receive the event with - * target=childObj (the original target) and currentTarget=parentObj (where the listener was added). - * @property currentTarget - * @type Object - * @default null - * @readonly - */ - p.currentTarget = null; - - /** - * For bubbling events, this indicates the current event phase:<OL> - * <LI> capture phase: starting from the top parent to the target</LI> - * <LI> at target phase: currently being dispatched from the target</LI> - * <LI> bubbling phase: from the target to the top parent</LI> - * </OL> - * @property eventPhase - * @type Number - * @default 0 - * @readonly - */ - p.eventPhase = 0; - - /** - * Indicates whether the event will bubble through the display list. - * @property bubbles - * @type Boolean - * @default false - * @readonly - */ - p.bubbles = false; - - /** - * Indicates whether the default behaviour of this event can be cancelled via - * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor. - * @property cancelable - * @type Boolean - * @default false - * @readonly - */ - p.cancelable = false; - - /** - * The epoch time at which this event was created. - * @property timeStamp - * @type Number - * @default 0 - * @readonly - */ - p.timeStamp = 0; - - /** - * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called - * on this event. - * @property defaultPrevented - * @type Boolean - * @default false - * @readonly - */ - p.defaultPrevented = false; - - /** - * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or - * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event. - * @property propagationStopped - * @type Boolean - * @default false - * @readonly - */ - p.propagationStopped = false; - - /** - * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called - * on this event. - * @property immediatePropagationStopped - * @type Boolean - * @default false - * @readonly - */ - p.immediatePropagationStopped = false; - - /** - * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event. - * @property removed - * @type Boolean - * @default false - * @readonly - */ - p.removed = false; - // constructor: /** - * Initialization method. - * @method initialize + * Contains properties and methods shared by all events for use with + * {{#crossLink "EventDispatcher"}}{{/crossLink}}. + * + * Note that Event objects are often reused, so you should never + * rely on an event object's state outside of the call stack it was received in. + * @class Event * @param {String} type The event type. * @param {Boolean} bubbles Indicates whether the event will bubble through the display list. * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled. - * @protected + * @constructor **/ - p.initialize = function(type, bubbles, cancelable) { + function Event(type, bubbles, cancelable) { + + + // public properties: + /** + * The type of event. + * @property type + * @type String + **/ this.type = type; - this.bubbles = bubbles; - this.cancelable = cancelable; + + /** + * The object that generated an event. + * @property target + * @type Object + * @default null + * @readonly + */ + this.target = null; + + /** + * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will + * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event + * is generated from childObj, then a listener on parentObj would receive the event with + * target=childObj (the original target) and currentTarget=parentObj (where the listener was added). + * @property currentTarget + * @type Object + * @default null + * @readonly + */ + this.currentTarget = null; + + /** + * For bubbling events, this indicates the current event phase:<OL> + * <LI> capture phase: starting from the top parent to the target</LI> + * <LI> at target phase: currently being dispatched from the target</LI> + * <LI> bubbling phase: from the target to the top parent</LI> + * </OL> + * @property eventPhase + * @type Number + * @default 0 + * @readonly + */ + this.eventPhase = 0; + + /** + * Indicates whether the event will bubble through the display list. + * @property bubbles + * @type Boolean + * @default false + * @readonly + */ + this.bubbles = !!bubbles; + + /** + * Indicates whether the default behaviour of this event can be cancelled via + * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor. + * @property cancelable + * @type Boolean + * @default false + * @readonly + */ + this.cancelable = !!cancelable; + + /** + * The epoch time at which this event was created. + * @property timeStamp + * @type Number + * @default 0 + * @readonly + */ this.timeStamp = (new Date()).getTime(); - }; + + /** + * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called + * on this event. + * @property defaultPrevented + * @type Boolean + * @default false + * @readonly + */ + this.defaultPrevented = false; + + /** + * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or + * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event. + * @property propagationStopped + * @type Boolean + * @default false + * @readonly + */ + this.propagationStopped = false; + + /** + * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called + * on this event. + * @property immediatePropagationStopped + * @type Boolean + * @default false + * @readonly + */ + this.immediatePropagationStopped = false; + + /** + * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event. + * @property removed + * @type Boolean + * @default false + * @readonly + */ + this.removed = false; + } + var p = Event.prototype; + // public methods: - /** * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true. * Mirrors the DOM event standard. * @method preventDefault **/ p.preventDefault = function() { - this.defaultPrevented = true; + this.defaultPrevented = this.cancelable&&true; }; /** @@ -695,321 +883,2067 @@ Event.prototype.constructor = Event; return "[Event (type="+this.type+")]"; }; -createjs.Event = Event; + createjs.Event = Event; }()); -/* -* IndexOf -* Visit http://createjs.com/ for documentation, updates and examples. -* -* Copyright (c) 2010 gskinner.com, inc. -* -* Permission is hereby granted, free of charge, to any person -* obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without -* restriction, including without limitation the rights to use, -* copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following -* conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -*/ -/** - * @module CreateJS - */ +//############################################################################## +// ErrorEvent.js +//############################################################################## -// namespace: this.createjs = this.createjs||{}; -/** - * @class Utility Methods - */ (function() { "use strict"; - /* - * Employs Duff's Device to make a more performant implementation of indexOf. - * see http://jsperf.com/duffs-indexof/2 - * #method indexOf - * @param {Array} array Array to search for searchElement - * @param searchElement Element to search array for. - * @return {Number} The position of the first occurrence of a specified value searchElement in the passed in array ar. + /** + * A general error event, which describes an error that occurred, as well as any details. + * @class ErrorEvent + * @param {String} [title] The error title + * @param {String} [message] The error description + * @param {Object} [data] Additional error data * @constructor */ - /* replaced with simple for loop for now, perhaps will be researched further - createjs.indexOf = function (ar, searchElement) { - var l = ar.length; + function ErrorEvent(title, message, data) { + this.Event_constructor("error"); - var n = (l * 0.125) ^ 0; // 0.125 == 1/8, using multiplication because it's faster in some browsers // ^0 floors result - for (var i = 0; i < n; i++) { - if(searchElement === ar[i*8]) { return (i*8);} - if(searchElement === ar[i*8+1]) { return (i*8+1);} - if(searchElement === ar[i*8+2]) { return (i*8+2);} - if(searchElement === ar[i*8+3]) { return (i*8+3);} - if(searchElement === ar[i*8+4]) { return (i*8+4);} - if(searchElement === ar[i*8+5]) { return (i*8+5);} - if(searchElement === ar[i*8+6]) { return (i*8+6);} - if(searchElement === ar[i*8+7]) { return (i*8+7);} - } + /** + * The short error title, which indicates the type of error that occurred. + * @property title + * @type String + */ + this.title = title; - var n = l % 8; - for (var i = 0; i < n; i++) { - if (searchElement === ar[l-n+i]) { - return l-n+i; - } - } + /** + * The verbose error message, containing details about the error. + * @property message + * @type String + */ + this.message = message; - return -1; - } - */ - - /** - * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of - * that value. Returns -1 if value is not found. - * - * var i = createjs.indexOf(myArray, myElementToFind); - * - * @method indexOf - * @param {Array} array Array to search for searchElement - * @param searchElement Element to find in array. - * @return {Number} The first index of searchElement in array. - */ - createjs.indexOf = function (array, searchElement){ - for (var i = 0,l=array.length; i < l; i++) { - if (searchElement === array[i]) { - return i; - } - } - return -1; + /** + * Additional data attached to an error. + * @property data + * @type {Object} + */ + this.data = data; } -}());/* -* Proxy -* Visit http://createjs.com/ for documentation, updates and examples. -* -* Copyright (c) 2010 gskinner.com, inc. -* -* Permission is hereby granted, free of charge, to any person -* obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without -* restriction, including without limitation the rights to use, -* copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following -* conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -*/ + var p = createjs.extend(ErrorEvent, createjs.Event); -/** - * @module CreateJS - */ - -// namespace: -this.createjs = this.createjs||{}; - -/** - * Various utilities that the CreateJS Suite uses. Utilities are created as separate files, and will be available on the - * createjs namespace directly: - * - * <h4>Example</h4> - * myObject.addEventListener("change", createjs.proxy(myMethod, scope)); - * - * @class Utility Methods - * @main Utility Methods - */ - -(function() { - "use strict"; - - /** - * A function proxy for methods. By default, JavaScript methods do not maintain scope, so passing a method as a - * callback will result in the method getting called in the scope of the caller. Using a proxy ensures that the - * method gets called in the correct scope. - * - * Additional arguments can be passed that will be applied to the function when it is called. - * - * <h4>Example</h4> - * myObject.addEventListener("event", createjs.proxy(myHandler, this, arg1, arg2)); - * - * function myHandler(arg1, arg2) { - * // This gets called when myObject.myCallback is executed. - * } - * - * @method proxy - * @param {Function} method The function to call - * @param {Object} scope The scope to call the method name on - * @param {mixed} [arg] * Arguments that are appended to the callback for additional params. - * @public - * @static - */ - createjs.proxy = function (method, scope) { - var aArgs = Array.prototype.slice.call(arguments, 2); - return function () { - return method.apply(scope, Array.prototype.slice.call(arguments, 0).concat(aArgs)); - }; - } - -}());/* -* defineProperty -* Visit http://createjs.com/ for documentation, updates and examples. -* -* Copyright (c) 2010 gskinner.com, inc. -* -* Permission is hereby granted, free of charge, to any person -* obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without -* restriction, including without limitation the rights to use, -* copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following -* conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -*/ - -/** - * @module CreateJS - */ - -// namespace: -this.createjs = this.createjs||{}; - -/** - * @class Utility Methods - */ -(function() { - "use strict"; - - /** - * Boolean value indicating if Object.defineProperty is supported. - * - * @property definePropertySupported - * @type {Boolean} - * @default true - */ - var t = Object.defineProperty ? true : false; - - // IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors - var foo = {}; - try { - Object.defineProperty(foo, "bar", { - get: function() { - return this._bar; - }, - set: function(value) { - this._bar = value; - } - }); - } catch (e) { - t = false; + p.clone = function() { + return new createjs.ErrorEvent(this.title, this.message, this.data); }; - createjs.definePropertySupported = t; -}());/* - * Sound - * Visit http://createjs.com/ for documentation, updates and examples. - * - * - * Copyright (c) 2012 gskinner.com, inc. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ + createjs.ErrorEvent = createjs.promote(ErrorEvent, "Event"); +}()); + +//############################################################################## +// ProgressEvent.js +//############################################################################## -// namespace: this.createjs = this.createjs || {}; -/** - * The SoundJS library manages the playback of audio on the web. It works via plugins which abstract the actual audio - * implementation, so playback is possible on any platform without specific knowledge of what mechanisms are necessary - * to play sounds. - * - * To use SoundJS, use the public API on the {{#crossLink "Sound"}}{{/crossLink}} class. This API is for: - * <ul><li>Installing audio playback Plugins</li> - * <li>Registering (and preloading) sounds</li> - * <li>Creating and playing sounds</li> - * <li>Master volume, mute, and stop controls for all sounds at once</li> - * </ul> - * - * <b>Controlling Sounds</b><br /> - * Playing sounds creates {{#crossLink "SoundInstance"}}{{/crossLink}} instances, which can be controlled individually. - * <ul><li>Pause, resume, seek, and stop sounds</li> - * <li>Control a sound's volume, mute, and pan</li> - * <li>Listen for events on sound instances to get notified when they finish, loop, or fail</li> - * </ul> - * - * <h4>Feature Set Example</h4> - * createjs.Sound.alternateExtensions = ["mp3"]; - * createjs.Sound.addEventListener("fileload", createjs.proxy(this.loadHandler, this)); - * createjs.Sound.registerSound("path/to/mySound.ogg", "sound"); - * function loadHandler(event) { - * // This is fired for each sound that is registered. - * var instance = createjs.Sound.play("sound"); // play using id. Could also use full sourcepath or event.src. - * instance.addEventListener("complete", createjs.proxy(this.handleComplete, this)); - * instance.volume = 0.5; - * } - * - * <h4>Browser Support</h4> - * Audio will work in browsers which support WebAudio (<a href="http://caniuse.com/audio-api">http://caniuse.com/audio-api</a>) - * or HTMLAudioElement (<a href="http://caniuse.com/audio">http://caniuse.com/audio</a>). A Flash fallback can be added - * as well, which will work in any browser that supports the Flash player. - * @module SoundJS - * @main SoundJS - */ +(function (scope) { + "use strict"; + + /** + * A createjs Event that is dispatched when progress changes. + * @class ProgressEvent + * @param {Number} loaded The amount that has been loaded. This can be any number relative to the total. + * @param {Number} [total] The total amount that will load. This will default to 0, so does not need to be passed in, + * as long as the loaded value is a progress value (between 0 and 1). + * @constructor + */ + function ProgressEvent(loaded, total) { + this.Event_constructor("progress"); + + /** + * The amount that has been loaded (out of a total amount) + * @property loaded + * @type {Number} + */ + this.loaded = loaded; + + /** + * The total "size" of the load. + * @oroperty size + * @type {Number} + * @default 1 + */ + this.total = (total == null) ? 1 : total; + + /** + * The percentage (out of 1) that the load has been completed. This is calculated using `loaded/total`. + * @property progress + * @type {Number} + * @default 0 + */ + this.progress = (total == 0) ? 0 : this.loaded / this.total; + }; + + var p = createjs.extend(ProgressEvent, createjs.Event); + + /** + * Returns a clone of the ProgressEvent instance. + * @method clone + * @return {ProgressEvent} a clone of the Event instance. + **/ + p.clone = function() { + return new createjs.ProgressEvent(this.loaded, this.total); + }; + + createjs.ProgressEvent = createjs.promote(ProgressEvent, "Event"); + +}(window)); + +//############################################################################## +// LoadItem.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + /** + * @class LoadItem + * + * @constructor + */ + function LoadItem() { + /** + * The source of the file that is being loaded. This property is <b>required</b>. The source can + * either be a string (recommended), or an HTML tag.</li> + * + * @type {null} + */ + this.src = null; + + /** + * The source of the file that is being loaded. This property is <b>required</b>. The source can + * either be a string (recommended), or an HTML tag. + * + * Check {{#crossLink "DataTypes"}}DataTypes{{/crossLink}} for the full list of supported types. + * + * @type {String|HTMLMediaElement|HTMLImageElement|HTMLLinkElement} + */ + this.type = createjs.AbstractLoader.TEXT; + + /** + * A string identifier which can be used to reference the loaded object. + * + * @type {String|Number} + */ + this.id = null; + + /** + * Set to `true` to ensure this asset loads in the order defined in the manifest. This + * will happen when the max connections has been set above 1 (using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}), + * and will only affect other assets also defined as `maintainOrder`. Everything else will finish as it is + * loaded. Ordered items are combined with script tags loading in order when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} + * is set to `true`. + * + * @type {boolean} + */ + this.maintainOrder = false; + + /** + * Optional, used for JSONP requests, to define what method to call when the JSONP is loaded. + * + * @type {String} + */ + this.callback = null; + + /** + * An arbitrary data object, which is included with the loaded object + * + * @type {Object} + */ + this.data = null; + + /** + * uUsed to define if this request uses GET or POST when sending data to the server. The default value is "GET" + * + * @type {String} + */ + this.method = createjs.LoadItem.GET; + + /** + * Optional object of name/value pairs to send to the server. + * + * @type {Object} + */ + this.values = null; + + /** + * Optional object hash of headers to attach to an XHR request. PreloadJS will automatically + * attach some default headers when required, including Origin, Content-Type, and X-Requested-With. You may + * override the default headers if needed. + * + * @type {Object} + */ + this.headers = null; + + /** + * Default false; Set to true if you need to enable credentials for XHR requests. + * + * @type {boolean} + */ + this.withCredentials = false; + + /** + * String, Default for text bases files (json, xml, text, css, js) "text/plain; charset=utf-8"; Sets the mime type of XHR. + * + * @type {String} + */ + this.mimeType = null; + + /** + * Sets the crossorigin attribute on images. + * + * @default Anonymous + * + * @type {boolean} + */ + this.crossOrigin = "Anonymous"; + + /** + * how long before we stop a request. Only applies to Tag loading and XHR level one loading. + * + * @type {number} + */ + this.loadTimeout = 8000; + }; + + var p = LoadItem.prototype = {}; + var s = LoadItem; + + s.create = function (value) { + if (typeof value == "string") { + var item = new LoadItem(); + item.src = value; + return item; + } else if (value instanceof s) { + return value; + } else if (value instanceof Object) { // Don't modify object, allows users to attach random data to the item. + return value; + } else { + throw new Error("Type not recognized."); + } + }; + + /** + * Provides a chainable shortcut method for setting a number of properties on the instance. + * + * <h4>Example</h4> + * + * var loadItem = new createjs.LoadItem().set({src:"image.png", maintainOrder:true}); + * + * @method set + * @param {Object} props A generic object containing properties to copy to the LoadItem instance. + * @return {LoadItem} Returns the instance the method is called on (useful for chaining calls.) + */ + p.set = function(props) { + for (var n in props) { this[n] = props[n]; } + return this; + }; + + createjs.LoadItem = s; + +}()); + +//############################################################################## +// RequestUtils.js +//############################################################################## (function () { + var s = {}; + + /** + * The Regular Expression used to test file URLS for an absolute path. + * @property ABSOLUTE_PATH + * @static + * @type {RegExp} + * @since 0.4.2 + */ + s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i; + + /** + * The Regular Expression used to test file URLS for an absolute path. + * @property RELATIVE_PATH + * @static + * @type {RegExp} + * @since 0.4.2 + */ + s.RELATIVE_PATT = (/^[./]*?\//i); + + /** + * The Regular Expression used to test file URLS for an extension. Note that URIs must already have the query string + * removed. + * @property EXTENSION_PATT + * @static + * @type {RegExp} + * @since 0.4.2 + */ + s.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i; + + /** + * @method _parseURI + * Parse a file path to determine the information we need to work with it. Currently, PreloadJS needs to know: + * <ul> + * <li>If the path is absolute. Absolute paths start with a protocol (such as `http://`, `file://`, or + * `//networkPath`)</li> + * <li>If the path is relative. Relative paths start with `../` or `/path` (or similar)</li> + * <li>The file extension. This is determined by the filename with an extension. Query strings are dropped, and + * the file path is expected to follow the format `name.ext`.</li> + * </ul> + * + * <strong>Note:</strong> This has changed from earlier versions, which used a single, complicated Regular Expression, which + * was difficult to maintain, and over-aggressive in determining all file properties. It has been simplified to + * only pull out what it needs. + * @param path + * @returns {Object} An Object with an `absolute` and `relative` Boolean, as well as an optional 'extension` String + * property, which is the lowercase extension. + * @private + */ + s.parseURI = function (path) { + var info = {absolute: false, relative: false}; + if (path == null) { return info; } + + // Drop the query string + var queryIndex = path.indexOf("?"); + if (queryIndex > -1) { + path = path.substr(0, queryIndex); + } + + // Absolute + var match; + if (s.ABSOLUTE_PATT.test(path)) { + info.absolute = true; + + // Relative + } else if (s.RELATIVE_PATT.test(path)) { + info.relative = true; + } + + // Extension + if (match = path.match(s.EXTENSION_PATT)) { + info.extension = match[1].toLowerCase(); + } + return info; + }; + + /** + * Formats an object into a query string for either a POST or GET request. + * @method _formatQueryString + * @param {Object} data The data to convert to a query string. + * @param {Array} [query] Existing name/value pairs to append on to this query. + * @private + */ + s.formatQueryString = function (data, query) { + if (data == null) { + throw new Error('You must specify data.'); + } + var params = []; + for (var n in data) { + params.push(n + '=' + escape(data[n])); + } + if (query) { + params = params.concat(query); + } + return params.join('&'); + }; + + /** + * A utility method that builds a file path using a source and a data object, and formats it into a new path. All + * of the loaders in PreloadJS use this method to compile paths when loading. + * @method buildPath + * @param {String} src The source path to add values to. + * @param {Object} [data] Object used to append values to this request as a query string. Existing parameters on the + * path will be preserved. + * @returns {string} A formatted string that contains the path and the supplied parameters. + * @since 0.3.1 + */ + s.buildPath = function (src, data) { + if (data == null) { + return src; + } + + var query = []; + var idx = src.indexOf('?'); + + if (idx != -1) { + var q = src.slice(idx + 1); + query = query.concat(q.split('&')); + } + + if (idx != -1) { + return src.slice(0, idx) + '?' + this._formatQueryString(data, query); + } else { + return src + '?' + this._formatQueryString(data, query); + } + }; + + /** + * @method _isCrossDomain + * @param {Object} item A load item with a `src` property + * @return {Boolean} If the load item is loading from a different domain than the current location. + * @private + */ + s.isCrossDomain = function (item) { + var target = document.createElement("a"); + target.href = item.src; + + var host = document.createElement("a"); + host.href = location.href; + + var crossdomain = (target.hostname != "") && + (target.port != host.port || + target.protocol != host.protocol || + target.hostname != host.hostname); + return crossdomain; + }; + + /** + * @method _isLocal + * @param {Object} item A load item with a `src` property + * @return {Boolean} If the load item is loading from the "file:" protocol. Assume that the host must be local as + * well. + * @private + */ + s.isLocal = function (item) { + var target = document.createElement("a"); + target.href = item.src; + return target.hostname == "" && target.protocol == "file:"; + }; + + /** + * Determine if a specific type should be loaded as a binary file. Currently, only images and items marked + * specifically as "binary" are loaded as binary. Note that audio is <b>not</b> a binary type, as we can not play + * back using an audio tag if it is loaded as binary. Plugins can change the item type to binary to ensure they get + * a binary result to work with. Binary files are loaded using XHR2. + * @method isBinary + * @param {String} type The item type. + * @return {Boolean} If the specified type is binary. + * @private + */ + s.isBinary = function (type) { + switch (type) { + case createjs.AbstractLoader.IMAGE: + case createjs.AbstractLoader.BINARY: + return true; + default: + return false; + } + }; + + /** + * Utility function to check if item is a valid HTMLImageElement + * + * @param item {object} + * @returns {boolean} + */ + s.isImageTag = function(item) { + return item instanceof HTMLImageElement; + }; + + /** + * Utility function to check if item is a valid HTMLAudioElement + * + * @param item + * @returns {boolean} + */ + s.isAudioTag = function(item) { + if (window.HTMLAudioElement) { + return item instanceof HTMLAudioElement; + } else { + return false; + } + }; + + /** + * Utility function to check if item is a valid HTMLVideoElement + * + * @param item + * @returns {boolean} + */ + s.isVideoTag = function(item) { + if (window.HTMLVideoElement) { + return item instanceof HTMLVideoElement; + } else { + false; + } + }; + + /** + * Determine if a specific type is a text based asset, and should be loaded as UTF-8. + * @method isText + * @param {String} type The item type. + * @return {Boolean} If the specified type is text. + * @private + */ + s.isText = function (type) { + switch (type) { + case createjs.AbstractLoader.TEXT: + case createjs.AbstractLoader.JSON: + case createjs.AbstractLoader.MANIFEST: + case createjs.AbstractLoader.XML: + case createjs.AbstractLoader.HTML: + case createjs.AbstractLoader.CSS: + case createjs.AbstractLoader.SVG: + case createjs.AbstractLoader.JAVASCRIPT: + return true; + default: + return false; + } + }; + + /** + * Determine the type of the object using common extensions. Note that the type can be passed in with the load item + * if it is an unusual extension. + * @param {String} extension The file extension to use to determine the load type. + * @return {String} The determined load type (for example, <code>AbstractLoader.IMAGE</code> or null if it can not be + * determined by the extension. + * @private + */ + s.getTypeByExtension = function (extension) { + if (extension == null) { + return createjs.AbstractLoader.TEXT; + } + + switch (extension.toLowerCase()) { + case "jpeg": + case "jpg": + case "gif": + case "png": + case "webp": + case "bmp": + return createjs.AbstractLoader.IMAGE; + case "ogg": + case "mp3": + case "webm": + return createjs.AbstractLoader.SOUND; + case "mp4": + case "webm": + case "ts": + return createjs.AbstractLoader.VIDEO; + case "json": + return createjs.AbstractLoader.JSON; + case "xml": + return createjs.AbstractLoader.XML; + case "css": + return createjs.AbstractLoader.CSS; + case "js": + return createjs.AbstractLoader.JAVASCRIPT; + case 'svg': + return createjs.AbstractLoader.SVG; + default: + return createjs.AbstractLoader.TEXT; + } + }; + + createjs.RequestUtils = s; + +}()); + +//############################################################################## +// AbstractLoader.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + +// constructor + /** + * The base loader, which defines all the generic methods, properties, and events. All loaders extend this class, + * including the {{#crossLink "LoadQueue"}}{{/crossLink}}. + * @class AbstractLoader + * @param {LoadItem|object|string} The item to be loaded. + * @param {Boolean} [preferXHR] Determines if the LoadItem should <em>try</em> and load using XHR, or take a + * tag-based approach, which can be better in cross-domain situations. Not all loaders can load using one or the + * other, so this is a suggested directive. + * @oaram {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class, + * such as {{#crossLink "IMAGE:property"}}{{/crossLink}}, {{#crossLink "CSS:property"}}{{/crossLink}}, etc. + * @extends EventDispatcher + */ + function AbstractLoader(loadItem, preferXHR, type) { + this.EventDispatcher_constructor(); + + // public properties + /** + * If the loader has completed loading. This provides a quick check, but also ensures that the different approaches + * used for loading do not pile up resulting in more than one <code>complete</code> event. + * @property loaded + * @type {Boolean} + * @default false + */ + this.loaded = false; + + /** + * Determine if the loader was canceled. Canceled loads will not fire complete events. Note that + * {{#crossLink "LoadQueue"}}{{/crossLink}} queues should be closed using {{#crossLink "AbstractLoader/close"}}{{/crossLink}} + * instead of setting this property. + * @property canceled + * @type {Boolean} + * @default false + */ + this.canceled = false; + + /** + * The current load progress (percentage) for this item. This will be a number between 0 and 1. + * + * <h4>Example</h4> + * + * var queue = new createjs.LoadQueue(); + * queue.loadFile("largeImage.png"); + * queue.on("progress", function() { + * console.log("Progress:", queue.progress, event.progress); + * }); + * + * @property progress + * @type {Number} + * @default 0 + */ + this.progress = 0; + + /** + * The type of this item. + * See {{#crossLink}}DataTypes{{/crossLink}} for a full list of supported types. + * + * @type {null} + */ + this.type = type; + + // protected properties + /** + * The item this loader represents. Note that this is null in a {{#crossLink "LoadQueue"}}{{/crossLink}}, but will + * be available on loaders such as {{#crossLink "XMLLoader"}}{{/crossLink}} and {{#crossLink "ImageLoader"}}{{/crossLink}}. + * @property _item + * @type {Object} + * @private + */ + if (loadItem) { + this._item = createjs.LoadItem.create(loadItem); + } else { + this._item = null; + } + + this._preferXHR = preferXHR; + + this._rawResult = null; + + /** + * A list of items that loaders load behind the scenes. This does not include the main item the loader is + * responsible for loading. Examples of loaders that have subitems include the {{#crossLink "SpriteSheetLoader"}}{{/crossLink}} and + * {{#crossLink "ManifestLoader"}}{{/crossLink}}. + * @property _loadItems + * @type {null} + * @protected + */ + this._loadedItems = null; + }; + + var p = createjs.extend(AbstractLoader, createjs.EventDispatcher); + var s = AbstractLoader; + + /** + * Defines a POST request, use for a method value when loading data. + * @property POST + * @type {string} + * @default post + */ + s.POST = "POST"; + + /** + * Defines a GET request, use for a method value when loading data. + * @property GET + * @type {string} + * @default get + */ + s.GET = "GET"; + + /** + * The preload type for generic binary types. Note that images are loaded as binary files when using XHR. + * @property BINARY + * @type {String} + * @default binary + * @static + * @since 0.6.0 + */ + s.BINARY = "binary"; + + /** + * The preload type for css files. CSS files are loaded using a <link> when loaded with XHR, or a + * <style> tag when loaded with tags. + * @property CSS + * @type {String} + * @default css + * @static + * @since 0.6.0 + */ + s.CSS = "css"; + + /** + * The preload type for image files, usually png, gif, or jpg/jpeg. Images are loaded into an <image> tag. + * @property IMAGE + * @type {String} + * @default image + * @static + * @since 0.6.0 + */ + s.IMAGE = "image"; + + /** + * The preload type for javascript files, usually with the "js" file extension. JavaScript files are loaded into a + * <script> tag. + * + * Since version 0.4.1+, due to how tag-loaded scripts work, all JavaScript files are automatically injected into + * the body of the document to maintain parity between XHR and tag-loaded scripts. In version 0.4.0 and earlier, + * only tag-loaded scripts are injected. + * @property JAVASCRIPT + * @type {String} + * @default javascript + * @static + * @since 0.6.0 + */ + s.JAVASCRIPT = "javascript"; + + /** + * The preload type for json files, usually with the "json" file extension. JSON data is loaded and parsed into a + * JavaScript object. Note that if a `callback` is present on the load item, the file will be loaded with JSONP, + * no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to, and the JSON + * must contain a matching wrapper function. + * @property JSON + * @type {String} + * @default json + * @static + * @since 0.6.0 + */ + s.JSON = "json"; + + /** + * The preload type for jsonp files, usually with the "json" file extension. JSON data is loaded and parsed into a + * JavaScript object. You are required to pass a callback parameter that matches the function wrapper in the JSON. + * Note that JSONP will always be used if there is a callback present, no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} + * property is set to. + * @property JSONP + * @type {String} + * @default jsonp + * @static + * @since 0.6.0 + */ + s.JSONP = "jsonp"; + + /** + * The preload type for json-based manifest files, usually with the "json" file extension. The JSON data is loaded + * and parsed into a JavaScript object. PreloadJS will then look for a "manifest" property in the JSON, which is an + * Array of files to load, following the same format as the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} + * method. If a "callback" is specified on the manifest object, then it will be loaded using JSONP instead, + * regardless of what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to. + * @property MANIFEST + * @type {String} + * @default manifest + * @static + * @since 0.6.0 + */ + s.MANIFEST = "manifest"; + + /** + * The preload type for sound files, usually mp3, ogg, or wav. When loading via tags, audio is loaded into an + * <audio> tag. + * @property SOUND + * @type {String} + * @default sound + * @static + * @since 0.6.0 + */ + s.SOUND = "sound"; + + /** + * The preload type for video files, usually mp4, ts, or ogg. When loading via tags, video is loaded into an + * <video> tag. + * @property VIDEO + * @type {String} + * @default video + * @static + * @since 0.6.0 + */ + s.VIDEO = "video"; + + /** + * The preload type for SpriteSheet files. SpriteSheet files are JSON files that contain string image paths. + * @property SPRITESHEET + * @type {String} + * @default spritesheet + * @static + * @since 0.6.0 + */ + s.SPRITESHEET = "spritesheet"; + + /** + * The preload type for SVG files. + * @property SVG + * @type {String} + * @default svg + * @static + * @since 0.6.0 + */ + s.SVG = "svg"; + + /** + * The preload type for text files, which is also the default file type if the type can not be determined. Text is + * loaded as raw text. + * @property TEXT + * @type {String} + * @default text + * @static + * @since 0.6.0 + */ + s.TEXT = "text"; + + /** + * The preload type for xml files. XML is loaded into an XML document. + * @property XML + * @type {String} + * @default xml + * @static + * @since 0.6.0 + */ + s.XML = "xml"; + +// Events + /** + * The {{#crossLink "ProgressEvent"}}{{/crossLink}} that is fired when the overall progress changes. Prior to + * version 0.6.0, this was just a regular {{#crossLink "Event"}}{{/crossLink}}. + * @event progress + * @since 0.3.0 + */ + + /** + * The {{#crossLink "Event"}}{{/crossLink}} that is fired when a load starts. + * @event loadstart + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @since 0.3.1 + */ + + /** + * The {{#crossLink "Event"}}{{/crossLink}} that is fired when the entire queue has been loaded. + * @event complete + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @since 0.3.0 + */ + + /** + * The {{#crossLink "ErrorEvent"}}{{/crossLink}} that is fired when the loader encounters an error. If the error was + * encountered by a file, the event will contain the item that caused the error. Prior to version 0.6.0, this was + * just a regular {{#crossLink "Event"}}{{/crossLink}}. + * @event error + * @since 0.3.0 + */ + + /** + * The {{#crossLink "Event"}}{{/crossLink}} that is fired when the loader encounters an internal file load error. + * This enables loaders to maintain internal queues, and surface file load errors. + * @event fileerror + * @param {Object} target The object that dispatched the event. + * @param {String} type The even type ("fileerror") + * @param {LoadItem|object} The item that encountered the error + * @since 0.6.0 + */ + + /** + * The {{#crossLink "Event"}}{{/crossLink}} that is fired when a loader internally loads a file. This enables + * loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}} to maintain internal {{#crossLink "LoadQueue"}}{{/crossLink}}s + * and notify when they have loaded a file. The {{#crossLink "LoadQueue"}}{{/crossLink}} class dispatches a + * slightly different {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event. + * @event fileload + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type ("fileload") + * @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} + * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the + * object will contain that value as a `src` property. + * @param {Object} result The HTML tag or parsed result of the loaded item. + * @param {Object} rawResult The unprocessed result, usually the raw text or binary data before it is converted + * to a usable object. + * @since 0.6.0 + */ + + /** + * The {{#crossLink "Event"}}{{/crossLink}} that is fired after the internal request is created, but before a load. + * This allows updates to the loader for specific loading needs, such as binary or XHR image loading. + * @event initialize + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type ("initialize") + * @param {AbstractLoader} loader The loader that has been initialized. + */ + + + /** + * Get a reference to the manifest item that is loaded by this loader. In some cases this will be the value that was + * passed into {{#crossLink "LoadQueue"}}{{/crossLink}} using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or + * {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. However if only a String path was passed in, then it will + * be a {{#crossLink "LoadItem"}}{{/crossLink}}. + * @method getItem + * @return {Object} The manifest item that this loader is responsible for loading. + * @since 0.6.0 + */ + p.getItem = function () { + return this._item; + }; + + /** + * Get a reference to the content that was loaded by the loader (only available after the {{#crossLink "complete:event"}}{{/crossLink}} + * event is dispatched. + * @method getResult + * @param {Boolean} [raw=false] Determines if the returned result will be the formatted content, or the raw loaded + * data (if it exists). + * @return {Object} + * @since 0.6.0 + */ + p.getResult = function (raw) { + return raw ? this._rawResult : this._result; + }; + + /** + * Return the `tag` this object creates or uses for loading. + * @method getTag + * @return {Object} The tag instance + * @since 0.6.0 + */ + p.getTag = function () { + return this._tag; + }; + + /** + * Set the `tag` this item uses for loading. + * @method setTag + * @param {Object} tag The tag instance + * @since 0.6.0 + */ + p.setTag = function(tag) { + this._tag = tag; + }; + + /** + * Begin loading the item. This method is required when using a loader by itself. + * + * <h4>Example</h4> + * + * var queue = new createjs.LoadQueue(); + * queue.addEventListener("complete", handleComplete); + * queue.loadManifest(fileArray, false); // Note the 2nd argument that tells the queue not to start loading yet + * queue.load(); + * + * @method load + */ + p.load = function () { + this._createRequest(); + + this._request.on("complete", this, this); + this._request.on("progress", this, this); + this._request.on("loadStart", this, this); + this._request.on("abort", this, this); + this._request.on("timeout", this, this); + this._request.on("error", this, this); + + var evt = new createjs.Event("initialize"); + evt.loader = this._request; + this.dispatchEvent(evt); + + this._request.load(); + }; + + /** + * Close the the item. This will stop any open requests (although downloads using HTML tags may still continue in + * the background), but events will not longer be dispatched. + * @method cancel + */ + p.cancel = function () { + this.canceled = true; + this.destroy(); + }; + + /** + * Clean up the loader. + * @method destroy + */ + p.destroy = function() { + if (this._request) { + this._request.removeAllEventListeners(); + this._request.destroy(); + } + + this._request = null; + + this._item = null; + this._rawResult = null; + this._result = null; + + this._loadItems = null; + + this.removeAllEventListeners(); + }; + + /** + * Get any items loaded internally by the loader. The enables loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}} + * to expose items it loads internally. + * @method getLoadedItems + * @return {Array} A list of the items loaded by the loader. + * @since 0.6.0 + */ + p.getLoadedItems = function () { + return this._loadedItems; + }; + + + // Private methods + /** + * Create an internal request used for loading. By default, an {{#crossLink "XHRRequest"}}{{/crossLink}} or + * {{#crossLink "TagRequest"}}{{/crossLink}} is created, depending on the value of {{#crossLink "preferXHR:property"}}{{/crossLink}}. + * Other loaders may override this to use different request types, such as {{#crossLink "ManifestLoader"}}{{/crossLink}}, + * which uses {{#crossLink "JSONLoader"}}{{/crossLink}} or {{#crossLink "JSONPLoader"}}{{/crossLink}} under the hood. + * @method _createRequest + * @private + */ + p._createRequest = function() { + if (!this._preferXHR) { + this._request = new createjs.TagRequest(this._item, false, this._tag || this._createTag(), this._tagSrcAttribute); + } else { + this._request = new createjs.XHRRequest(this._item, false); + } + }; + + /** + * Dispatch a loadstart {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/loadstart:event"}}{{/crossLink}} + * event for details on the event payload. + * @method _sendLoadStart + * @protected + */ + p._sendLoadStart = function () { + if (this._isCanceled()) { return; } + this.dispatchEvent("loadstart"); + }; + + /** + * Dispatch a {{#crossLink "ProgressEvent"}}{{/crossLink}}. + * @method _sendProgress + * @param {Number | Object} value The progress of the loaded item, or an object containing <code>loaded</code> + * and <code>total</code> properties. + * @protected + */ + p._sendProgress = function (value) { + if (this._isCanceled()) { return; } + var event = null; + if (typeof(value) == "number") { + this.progress = value; + event = new createjs.ProgressEvent(this.progress); + } else { + event = value; + this.progress = value.loaded / value.total; + event.progress = this.progress; + if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; } + } + this.hasEventListener("progress") && this.dispatchEvent(event); + }; + + /** + * Dispatch a complete {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}} event + * @method _sendComplete + * @protected + */ + p._sendComplete = function () { + if (this._isCanceled()) { return; } + + this.loaded = true; + + var event = new createjs.Event("complete"); + event.rawResult = this._rawResult; + + if (this._result != null) { + event.result = this._result; + } + + this.dispatchEvent(event); + }; + + /** + * Dispatch an error {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}} + * event for details on the event payload. + * @method _sendError + * @param {ErrorEvent} event The event object containing specific error properties. + * @protected + */ + p._sendError = function (event) { + if (this._isCanceled() || !this.hasEventListener("error")) { return; } + if (event == null) { + event = new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"); // TODO: Populate error + } + this.dispatchEvent(event); + }; + + /** + * Determine if the load has been canceled. This is important to ensure that method calls or asynchronous events + * do not cause issues after the queue has been cleaned up. + * @method _isCanceled + * @return {Boolean} If the loader has been canceled. + * @protected + */ + p._isCanceled = function () { + if (window.createjs == null || this.canceled) { + return true; + } + return false; + }; + + /** + * A custom result formatter function, which is called just before a request dispatches its complete event. Most + * loader types already have an internal formatter, but this can be user-overridden for custom formatting. The + * formatted result will be available on Loaders using {{#crossLink "getResult"}}{{/crossLink}}, and passing `true`. + * @property resultFormatter + * @type Function + * @return {Object} The formatted result + * @since 0.6.0 + */ + p.resultFormatter = null; //TODO: Add support for async formatting. + + /** + * Handle events from internal requests. By default, loaders will handle, and redispatch the necessary events, but + * this method can be overridden for custom behaviours. + * @method handleEvent + * @param {Event} The event that the internal request dispatches. + * @private + * @since 0.6.0 + */ + p.handleEvent = function (event) { + switch (event.type) { + case "complete": + this._rawResult = event.target._response; + var result = this.resultFormatter && this.resultFormatter(this); + var _this = this; + if (result instanceof Function) { + result(function(result) { + _this._result = result; + _this._sendComplete(); + }); + } else { + this._result = result || this._rawResult; + this._sendComplete(); + } + break; + case "progress": + this._sendProgress(event); + break; + case "error": + this._sendError(event); + break; + case "loadstart": + this._sendLoadStart(); + break; + case "abort": + case "timeout": + if (!this._isCanceled()) { + this.dispatchEvent(event.type); + } + break; + } + }; + + /** + * @method buildPath + * @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}} + * instead. + */ + p.buildPath = function (src, data) { + return createjs.RequestUtils.buildPath(src, data); + }; + + /** + * @method toString + * @return {String} a string representation of the instance. + */ + p.toString = function () { + return "[PreloadJS AbstractLoader]"; + }; + + createjs.AbstractLoader = createjs.promote(AbstractLoader, "EventDispatcher"); + +}()); + +//############################################################################## +// AbstractMediaLoader.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + // constructor + /** + * The AbstractMediaLoader class description goes here. + * + */ + function AbstractMediaLoader(loadItem, preferXHR, type) { + this.AbstractLoader_constructor(loadItem, preferXHR, type); + + // public properties + + // protected properties + this._tagSrcAttribute = "src"; + + /** + * Used to determine what type of tag to create, for example "audio" + * @property _tagType + * @type {string} + * @private + */ + this._tagType = type; + + this.resultFormatter = this._formatResult; + }; + + var p = createjs.extend(AbstractMediaLoader, createjs.AbstractLoader); + // static properties + + // public methods + + // protected methods + p.load = function () { + // TagRequest will handle most of this, but Sound / Video need a few custom properties, so just handle them here. + if (!this._tag) { + this._tag = this._createTag(this._item.src); + } + + this._tag.preload = "auto"; + this._tag.load(); + + this.AbstractLoader_load(); + }; + + /** + * Abstract, create a new tag if none exist. + * + * @private + */ + p._createTag = function () { + + }; + + p._formatResult = function (loader) { + this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler); + this._tag.onstalled = null; + if (this._preferXHR) { + loader.getTag().src = loader.getResult(true); + } + return loader.getTag(); + }; + + createjs.AbstractMediaLoader = createjs.promote(AbstractMediaLoader, "AbstractLoader"); + +}()); + +//############################################################################## +// AbstractRequest.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + var AbstractRequest = function (item) { + this._item = item; + }; + + var p = createjs.extend(AbstractRequest, createjs.EventDispatcher); + var s = AbstractRequest; + + /** + * Abstract function. + * + */ + p.load = function() { + + }; + + p.destroy = function() { + + }; + + p.cancel = function() { + + }; + + createjs.AbstractRequest = createjs.promote(AbstractRequest, "EventDispatcher"); + +}()); + +//############################################################################## +// TagRequest.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + // constructor + /** + * The TagRequest class description goes here. + * + */ + function TagRequest(loadItem, preferXHR, tag, srcAttribute) { + this.AbstractRequest_constructor(loadItem, preferXHR); + + // public properties + + // protected properties + this._tag = tag; + this._tagSrcAttribute = srcAttribute; + + this._loadedHandler = createjs.proxy(this._handleTagComplete, this); + }; + + var p = createjs.extend(TagRequest, createjs.AbstractRequest); + var s = TagRequest; + + p.load = function () { + window.document.body.appendChild(this._tag); + + this._tag.onload = createjs.proxy(this._handleTagComplete, this); + this._tag.onreadystatechange = createjs.proxy(this._handleReadyStateChange, this); + + var evt = new createjs.Event("initialize"); + evt.loader = this._tag; + + this.dispatchEvent(evt); + + this._tag[this._tagSrcAttribute] = this._item.src; + }; + + p.destroy = function() { + this._clean(); + this._tag = null; + + this.AbstractRequest_destory(); + }; + + /** + * Handle the readyStateChange event from a tag. We sometimes need this in place of the onload event (mainly SCRIPT + * and LINK tags), but other cases may exist. + * @method _handleReadyStateChange + * @private + */ + p._handleReadyStateChange = function () { + clearTimeout(this._loadTimeout); + // This is strictly for tags in browsers that do not support onload. + var tag = this._tag; + + // Complete is for old IE support. + if (tag.readyState == "loaded" || tag.readyState == "complete") { + this._handleTagComplete(); + } + }; + + p._handleTagComplete = function () { + this._rawResult = this._tag; + this._result = this.resultFormatter && this.resultFormatter(this) || this._rawResult; + + this._clean(); + + this.dispatchEvent("complete"); + }; + + /** + * Remove event listeners, but don't destory the request object + * + * @private + */ + p._clean = function() { + this._tag.onload = null; + this._tag.onreadystatechange = null; + }; + + /** + * Handle a stalled audio event. The main place we seem to get these is with HTMLAudio in Chrome when we try and + * playback audio that is already in a load, but not complete. + * @method _handleStalled + * @private + */ + p._handleStalled = function () { + //Ignore, let the timeout take care of it. Sometimes its not really stopped. + }; + + createjs.TagRequest = createjs.promote(TagRequest, "AbstractRequest"); + +}()); + +//############################################################################## +// MediaTagRequest.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + // constructor + /** + * The TagRequest class description goes here. + * + */ + function MediaTagRequest(loadItem, preferXHR, tag, srcAttribute) { + this.AbstractRequest_constructor(loadItem, preferXHR); + + // public properties + + // protected properties + this._tag = tag; + this._tagSrcAttribute = srcAttribute; + + this._loadedHandler = createjs.proxy(this._handleTagComplete, this); + }; + + var p = createjs.extend(MediaTagRequest, createjs.TagRequest); + var s = MediaTagRequest; + + p.load = function () { + this._tag.onstalled = createjs.proxy(this._handleStalled, this); + this._tag.onprogress = createjs.proxy(this._handleProgress, this); + + // This will tell us when audio is buffered enough to play through, but not when its loaded. + // The tag doesn't keep loading in Chrome once enough has buffered, and we have decided that behaviour is sufficient. + this._tag.addEventListener && this._tag.addEventListener("canplaythrough", this._loadedHandler); // canplaythrough callback doesn't work in Chrome, so we use an event. + + this.TagRequest_load(); + }; + + /** + * Handle the readyStateChange event from a tag. We sometimes need this in place of the onload event (mainly SCRIPT + * and LINK tags), but other cases may exist. + * @method _handleReadyStateChange + * @private + */ + p._handleReadyStateChange = function () { + clearTimeout(this._loadTimeout); + // This is strictly for tags in browsers that do not support onload. + var tag = this._tag; + + // Complete is for old IE support. + if (tag.readyState == "loaded" || tag.readyState == "complete") { + this._handleTagComplete(); + } + }; + + /** + * Handle a stalled audio event. The main place we seem to get these is with HTMLAudio in Chrome when we try and + * playback audio that is already in a load, but not complete. + * @method _handleStalled + * @private + */ + p._handleStalled = function () { + //Ignore, let the timeout take care of it. Sometimes its not really stopped. + }; + + /** + * The XHR request has reported progress. + * @method _handleProgress + * @param {Object} event The XHR progress event. + * @private + */ + p._handleProgress = function (event) { + if (!event || event.loaded > 0 && event.total == 0) { + return; // Sometimes we get no "total", so just ignore the progress event. + } + + var newEvent = new createjs.ProgressEvent(event.loaded, event.total); + this.dispatchEvent(newEvent); + }; + + /** + * + * @private + */ + p._clean = function () { + this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler); + this._tag.onstalled = null; + this._tag.onprogress = null; + + this.TagRequest__clean(); + }; + + createjs.MediaTagRequest = createjs.promote(MediaTagRequest, "TagRequest"); + +}()); + +//############################################################################## +// XHRRequest.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + +// constructor + /** + * A preloader that loads items using XHR requests, usually XMLHttpRequest. However XDomainRequests will be used + * for cross-domain requests if possible, and older versions of IE fall back on to ActiveX objects when necessary. + * XHR requests load the content as text or binary data, provide progress and consistent completion events, and + * can be canceled during load. Note that XHR is not supported in IE 6 or earlier, and is not recommended for + * cross-domain loading. + * @class XHRRequest + * @constructor + * @param {Object} item The object that defines the file to load. Please see the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} + * for an overview of supported file properties. + * @extends AbstractLoader + */ + function XHRRequest(item) { + this.AbstractRequest_constructor(item); + + // protected properties + /** + * A reference to the XHR request used to load the content. + * @property _request + * @type {XMLHttpRequest | XDomainRequest | ActiveX.XMLHTTP} + * @private + */ + this._request = null; + + /** + * A manual load timeout that is used for browsers that do not support the onTimeout event on XHR (XHR level 1, + * typically IE9). + * @property _loadTimeout + * @type {Number} + * @private + */ + this._loadTimeout = null; + + /** + * The browser's XHR (XMLHTTPRequest) version. Supported versions are 1 and 2. There is no official way to detect + * the version, so we use capabilities to make a best guess. + * @property _xhrLevel + * @type {Number} + * @default 1 + * @private + */ + this._xhrLevel = 1; + + /** + * The response of a loaded file. This is set because it is expensive to look up constantly. This property will be + * null until the file is loaded. + * @property _response + * @type {mixed} + * @private + */ + this._response = null; + + /** + * The response of the loaded file before it is modified. In most cases, content is converted from raw text to + * an HTML tag or a formatted object which is set to the <code>result</code> property, but the developer may still + * want to access the raw content as it was loaded. + * @property _rawResponse + * @type {String|Object} + * @private + */ + this._rawResponse = null; + + this._canceled = false; + + // Setup our event handlers now. + this._handleLoadStartProxy = createjs.proxy(this._handleLoadStart, this); + this._handleProgressProxy = createjs.proxy(this._handleProgress, this); + this._handleAbortProxy = createjs.proxy(this._handleAbort, this); + this._handleErrorProxy = createjs.proxy(this._handleError, this); + this._handleTimeoutProxy = createjs.proxy(this._handleTimeout, this); + this._handleLoadProxy = createjs.proxy(this._handleLoad, this); + this._handleReadyStateChangeProxy = createjs.proxy(this._handleReadyStateChange, this); + + if (!this._createXHR(item)) { + //TODO: Throw error? + } + }; + + var p = createjs.extend(XHRRequest, createjs.AbstractRequest); + +// static properties + /** + * A list of XMLHTTP object IDs to try when building an ActiveX object for XHR requests in earlier versions of IE. + * @property ACTIVEX_VERSIONS + * @type {Array} + * @since 0.4.2 + * @private + */ + XHRRequest.ACTIVEX_VERSIONS = [ + "Msxml2.XMLHTTP.6.0", + "Msxml2.XMLHTTP.5.0", + "Msxml2.XMLHTTP.4.0", + "MSXML2.XMLHTTP.3.0", + "MSXML2.XMLHTTP", + "Microsoft.XMLHTTP" + ]; + +// Public methods + /** + * Look up the loaded result. + * @method getResult + * @param {Boolean} [raw=false] Return a raw result instead of a formatted result. This applies to content + * loaded via XHR such as scripts, XML, CSS, and Images. If there is no raw result, the formatted result will be + * returned instead. + * @return {Object} A result object containing the content that was loaded, such as: + * <ul> + * <li>An image tag (<image />) for images</li> + * <li>A script tag for JavaScript (<script />). Note that scripts loaded with tags may be added to the + * HTML head.</li> + * <li>A style tag for CSS (<style />)</li> + * <li>Raw text for TEXT</li> + * <li>A formatted JavaScript object defined by JSON</li> + * <li>An XML document</li> + * <li>An binary arraybuffer loaded by XHR</li> + * </ul> + * Note that if a raw result is requested, but not found, the result will be returned instead. + */ + p.getResult = function (raw) { + if (raw && this._rawResponse) { + return this._rawResponse; + } + return this._response; + }; + + // Overrides abstract method in AbstractRequest + p.cancel = function () { + this.canceled = true; + this._clean(); + this._request.abort(); + }; + + // Overrides abstract method in AbstractLoader + p.load = function () { + if (this._request == null) { + this._handleError(); + return; + } + + //Events + this._request.addEventListener("loadstart", this._handleLoadStartProxy); + this._request.addEventListener("progress", this._handleProgressProxy); + this._request.addEventListener("abort", this._handleAbortProxy); + this._request.addEventListener("error",this._handleErrorProxy); + this._request.addEventListener("timeout", this._handleTimeoutProxy); + + // Note: We don't get onload in all browsers (earlier FF and IE). onReadyStateChange handles these. + this._request.addEventListener("load", this._handleLoadProxy); + this._request.addEventListener("readystatechange", this._handleReadyStateChangeProxy); + + // Set up a timeout if we don't have XHR2 + if (this._xhrLevel == 1) { + this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this.getItem().loadTimeout); + } + + // Sometimes we get back 404s immediately, particularly when there is a cross origin request. // note this does not catch in Chrome + try { + if (!this._item.values || this._item.method == createjs.AbstractLoader.GET) { + this._request.send(); + } else if (this._item.method == createjs.AbstractLoader.POST) { + this._request.send(createjs.RequestUtils.formatQueryString(this._item.values)); + } + } catch (error) { + this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND", null, error)); + } + }; + + p.setResponseType = function (type) { + this._request.responseType = type; + }; + + /** + * Get all the response headers from the XmlHttpRequest. + * + * <strong>From the docs:</strong> Return all the HTTP headers, excluding headers that are a case-insensitive match + * for Set-Cookie or Set-Cookie2, as a single string, with each header line separated by a U+000D CR U+000A LF pair, + * excluding the status line, and with each header name and header value separated by a U+003A COLON U+0020 SPACE + * pair. + * @method getAllResponseHeaders + * @return {String} + * @since 0.4.1 + */ + p.getAllResponseHeaders = function () { + if (this._request.getAllResponseHeaders instanceof Function) { + return this._request.getAllResponseHeaders(); + } else { + return null; + } + }; + + /** + * Get a specific response header from the XmlHttpRequest. + * + * <strong>From the docs:</strong> Returns the header field value from the response of which the field name matches + * header, unless the field name is Set-Cookie or Set-Cookie2. + * @method getResponseHeader + * @param {String} header The header name to retrieve. + * @return {String} + * @since 0.4.1 + */ + p.getResponseHeader = function (header) { + if (this._request.getResponseHeader instanceof Function) { + return this._request.getResponseHeader(header); + } else { + return null; + } + }; + +// protected methods + /** + * The XHR request has reported progress. + * @method _handleProgress + * @param {Object} event The XHR progress event. + * @private + */ + p._handleProgress = function (event) { + if (!event || event.loaded > 0 && event.total == 0) { + return; // Sometimes we get no "total", so just ignore the progress event. + } + + var newEvent = new createjs.ProgressEvent(event.loaded, event.total); + this.dispatchEvent(newEvent); + }; + + /** + * The XHR request has reported a load start. + * @method _handleLoadStart + * @param {Object} event The XHR loadStart event. + * @private + */ + p._handleLoadStart = function (event) { + clearTimeout(this._loadTimeout); + this.dispatchEvent("loadstart"); + }; + + /** + * The XHR request has reported an abort event. + * @method handleAbort + * @param {Object} event The XHR abort event. + * @private + */ + p._handleAbort = function (event) { + this._clean(); + this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED", null, event)); + }; + + /** + * The XHR request has reported an error event. + * @method _handleError + * @param {Object} event The XHR error event. + * @private + */ + p._handleError = function (event) { + this._clean(); + + + this.dispatchEvent(new createjs.ErrorEvent(null, null, event)); + }; + + /** + * The XHR request has reported a readyState change. Note that older browsers (IE 7 & 8) do not provide an onload + * event, so we must monitor the readyStateChange to determine if the file is loaded. + * @method _handleReadyStateChange + * @param {Object} event The XHR readyStateChange event. + * @private + */ + p._handleReadyStateChange = function (event) { + if (this._request.readyState == 4) { + this._handleLoad(); + } + }; + + /** + * The XHR request has completed. This is called by the XHR request directly, or by a readyStateChange that has + * <code>request.readyState == 4</code>. Only the first call to this method will be processed. + * @method _handleLoad + * @param {Object} event The XHR load event. + * @private + */ + p._handleLoad = function (event) { + if (this.loaded) { + return; + } + this.loaded = true; + + var error = this._checkError(); + if (error) { + this._handleError(error); + return; + } + + this._response = this._getResponse(); + this._clean(); + + this.dispatchEvent(new createjs.Event("complete")); + }; + + /** + * The XHR request has timed out. This is called by the XHR request directly, or via a <code>setTimeout</code> + * callback. + * @method _handleTimeout + * @param {Object} [event] The XHR timeout event. This is occasionally null when called by the backup setTimeout. + * @private + */ + p._handleTimeout = function (event) { + this._clean(); + + this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT", null, event)); + }; + +// Protected + /** + * Determine if there is an error in the current load. This checks the status of the request for problem codes. Note + * that this does not check for an actual response. Currently, it only checks for 404 or 0 error code. + * @method _checkError + * @return {int} If the request status returns an error code. + * @private + */ + p._checkError = function () { + //LM: Probably need additional handlers here, maybe 501 + var status = parseInt(this._request.status); + + switch (status) { + case 404: // Not Found + case 0: // Not Loaded + return new Error(status); + } + return null; + }; + + /** + * Validate the response. Different browsers have different approaches, some of which throw errors when accessed + * in other browsers. If there is no response, the <code>_response</code> property will remain null. + * @method _getResponse + * @private + */ + p._getResponse = function () { + if (this._response != null) { + return this._response; + } + + if (this._request.response != null) { + return this._request.response; + } + + // Android 2.2 uses .responseText + try { + if (this._request.responseText != null) { + return this._request.responseText; + } + } catch (e) { + } + + // When loading XML, IE9 does not return .response, instead it returns responseXML.xml + try { + if (this._request.responseXML != null) { + return this._request.responseXML; + } + } catch (e) { + } + + return null; + }; + + /** + * Create an XHR request. Depending on a number of factors, we get totally different results. + * <ol><li>Some browsers get an <code>XDomainRequest</code> when loading cross-domain.</li> + * <li>XMLHttpRequest are created when available.</li> + * <li>ActiveX.XMLHTTP objects are used in older IE browsers.</li> + * <li>Text requests override the mime type if possible</li> + * <li>Origin headers are sent for crossdomain requests in some browsers.</li> + * <li>Binary loads set the response type to "arraybuffer"</li></ol> + * @method _createXHR + * @param {Object} item The requested item that is being loaded. + * @return {Boolean} If an XHR request or equivalent was successfully created. + * @private + */ + p._createXHR = function (item) { + // Check for cross-domain loads. We can't fully support them, but we can try. + var crossdomain = createjs.RequestUtils.isCrossDomain(item); + var headers = {}; + + // Create the request. Fallback to whatever support we have. + var req = null; + if (window.XMLHttpRequest) { + req = new XMLHttpRequest(); + // This is 8 or 9, so use XDomainRequest instead. + if (crossdomain && req.withCredentials === undefined && window.XDomainRequest) { + req = new XDomainRequest(); + } + } else { // Old IE versions use a different approach + for (var i = 0, l = s.ACTIVEX_VERSIONS.length; i < l; i++) { + var axVersion = s.ACTIVEX_VERSIONS[i]; + try { + req = new ActiveXObject(axVersions); + break; + } catch (e) {} + } + if (req == null) { return false; } + } + + // IE9 doesn't support overrideMimeType(), so we need to check for it. + if (item.mimeType && req.overrideMimeType) { + req.overrideMimeType(item.mimeType); + } + + // Determine the XHR level + this._xhrLevel = (typeof req.responseType === "string") ? 2 : 1; + + var src = null; + if (item.method == createjs.AbstractLoader.GET) { + src = createjs.RequestUtils.buildPath(item.src, item.values); + } else { + src = item.src; + } + + // Open the request. Set cross-domain flags if it is supported (XHR level 1 only) + req.open(item.method || createjs.AbstractLoader.GET, src, true); + + if (crossdomain && req instanceof XMLHttpRequest && this._xhrLevel == 1) { + headers["Origin"] = location.origin; + } + + // To send data we need to set the Content-type header) + if (item.values && item.method == createjs.AbstractLoader.POST) { + headers["Content-Type"] = "application/x-www-form-urlencoded"; + } + + if (!crossdomain && !headers["X-Requested-With"]) { + headers["X-Requested-With"] = "XMLHttpRequest"; + } + + if (item.headers) { + for (var n in item.headers) { + headers[n] = item.headers[n]; + } + } + + for (n in headers) { + req.setRequestHeader(n, headers[n]) + } + + if (req instanceof XMLHttpRequest && item.withCredentials !== undefined) { + req.withCredentials = item.withCredentials; + } + + this._request = req; + + return true; + }; + + /** + * A request has completed (or failed or canceled), and needs to be disposed. + * @method _clean + * @private + */ + p._clean = function () { + clearTimeout(this._loadTimeout); + + this._request.removeEventListener("loadstart", this._handleLoadStartProxy); + this._request.removeEventListener("progress", this._handleProgressProxy); + this._request.removeEventListener("abort", this._handleAbortProxy); + this._request.removeEventListener("error",this._handleErrorProxy); + this._request.removeEventListener("timeout", this._handleTimeoutProxy); + this._request.removeEventListener("load", this._handleLoadProxy); + this._request.removeEventListener("readystatechange", this._handleReadyStateChangeProxy); + }; + + p.toString = function () { + return "[PreloadJS XHRRequest]"; + }; + + createjs.XHRRequest = createjs.promote(XHRRequest, "AbstractRequest"); + +}()); + +//############################################################################## +// SoundLoader.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + // constructor + /** + * The SoundLoader class description goes here. + * + */ + function SoundLoader(loadItem, preferXHR) { + this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SOUND); + + this._tagType = "audio"; + + if (createjs.RequestUtils.isAudioTag(loadItem) || createjs.RequestUtils.isAudioTag(loadItem.src)) { + this._preferXHR = false; + this._tag =createjs.RequestUtils.isAudioTag(loadItem)?loadItem:loadItem.src; + } + }; + + var p = createjs.extend(SoundLoader, createjs.AbstractMediaLoader); + var s = SoundLoader; + /** + * LoadQueue calls this when it creates loaders. + * Each loader has the option to say either yes (true) or no (false). + * + * @private + * @param item The LoadItem LoadQueue is trying to load. + * @returns {boolean} + */ + s.canLoadItem = function (item) { + return item.type == createjs.AbstractLoader.SOUND; + }; + + p._createRequest = function() { + if (!this._preferXHR) { + this._request = new createjs.MediaTagRequest(this._item, false, this._tag || this._createTag(), this._tagSrcAttribute); + } else { + this._request = new createjs.XHRRequest(this._item, false); + } + }; + + /** + * Create an HTML audio tag. + * @method _createTag + * @param {String} src The source file to set for the audio tag. + * @return {HTMLElement} Returns an HTML audio tag. + * @protected + */ + p._createTag = function (src) { + var tag = document.createElement(this._tagType); + tag.autoplay = false; + tag.preload = "none"; + + //LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works. + tag.src = src; + return tag; + }; + + createjs.SoundLoader = createjs.promote(SoundLoader, "AbstractMediaLoader"); + +}()); + +//############################################################################## +// Sound.js +//############################################################################## + +this.createjs = this.createjs || {}; + + + +(function () { "use strict"; /** @@ -1018,7 +2952,7 @@ this.createjs = this.createjs || {}; * * <b>Registering and Preloading</b><br /> * Before you can play a sound, it <b>must</b> be registered. You can do this with {{#crossLink "Sound/registerSound"}}{{/crossLink}}, - * or register multiple sounds using {{#crossLink "Sound/registerManifest"}}{{/crossLink}}. If you don't register a + * or register multiple sounds using {{#crossLink "Sound/registerSounds"}}{{/crossLink}}. If you don't register a * sound prior to attempting to play it using {{#crossLink "Sound/play"}}{{/crossLink}} or create it using {{#crossLink "Sound/createInstance"}}{{/crossLink}}, * the sound source will be automatically registered but playback will fail as the source will not be ready. If you use * <a href="http://preloadjs.com" target="_blank">PreloadJS</a>, registration is handled for you when the sound is @@ -1027,18 +2961,18 @@ this.createjs = this.createjs || {}; * * <b>Playback</b><br /> * To play a sound once it's been registered and preloaded, use the {{#crossLink "Sound/play"}}{{/crossLink}} method. - * This method returns a {{#crossLink "SoundInstance"}}{{/crossLink}} which can be paused, resumed, muted, etc. - * Please see the {{#crossLink "SoundInstance"}}{{/crossLink}} documentation for more on the instance control APIs. + * This method returns a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} which can be paused, resumed, muted, etc. + * Please see the {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} documentation for more on the instance control APIs. * * <b>Plugins</b><br /> * By default, the {{#crossLink "WebAudioPlugin"}}{{/crossLink}} or the {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}} * are used (when available), although developers can change plugin priority or add new plugins (such as the - * provided {{#crossLink "FlashPlugin"}}{{/crossLink}}). Please see the {{#crossLink "Sound"}}{{/crossLink}} API + * provided {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}). Please see the {{#crossLink "Sound"}}{{/crossLink}} API * methods for more on the playback and plugin APIs. To install plugins, or specify a different plugin order, see * {{#crossLink "Sound/installPlugins"}}{{/crossLink}}. * * <h4>Example</h4> - * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashPlugin]); + * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashAudioPlugin]); * createjs.Sound.alternateExtensions = ["mp3"]; * createjs.Sound.addEventListener("fileload", createjs.proxy(this.loadHandler, (this)); * createjs.Sound.registerSound("path/to/mySound.ogg", "sound"); @@ -1051,7 +2985,7 @@ this.createjs = this.createjs || {}; * * The maximum number of concurrently playing instances of the same sound can be specified in the "data" argument * of {{#crossLink "Sound/registerSound"}}{{/crossLink}}. Note that if not specified, the active plugin will apply - * a default limit. Currently HTMLAudioPlugin sets a default limit of 2, while WebAudioPlugin and FlashPlugin set a + * a default limit. Currently HTMLAudioPlugin sets a default limit of 2, while WebAudioPlugin and FlashAudioPlugin set a * default limit of 100. * * createjs.Sound.registerSound("sound.mp3", "soundId", 4); @@ -1066,7 +3000,7 @@ this.createjs = this.createjs || {}; * queue.installPlugin(createjs.Sound); * * <b>Audio Sprites</b><br /> - * SoundJS has added support for Audio Sprites, available as of version 0.5.3. + * SoundJS has added support for Audio Sprites, available as of version 0.6.0. * For those unfamiliar with audio sprites, they are much like CSS sprites or sprite sheets: multiple audio assets * grouped into a single file. * @@ -1089,7 +3023,7 @@ this.createjs = this.createjs || {}; * <h4>Example</h4> * createjs.Sound.initializeDefaultPlugins(); * var assetsPath = "./assets/"; - * var manifest = [{ + * var sounds = [{ * src:"MyAudioSprite.ogg", data: { * audioSprite: [ * {id:"sound1", startTime:0, duration:500}, @@ -1100,11 +3034,11 @@ this.createjs = this.createjs || {}; * ]; * createjs.Sound.alternateExtensions = ["mp3"]; * createjs.Sound.addEventListener("fileload", loadSound); - * createjs.Sound.registerManifest(manifest, assetsPath); + * createjs.Sound.registerSounds(sounds, assetsPath); * // after load is complete * createjs.Sound.play("sound2"); * - * You can also create audio sprites on the fly by setting the startTime and duration when creating an new SoundInstance. + * You can also create audio sprites on the fly by setting the startTime and duration when creating an new AbstractSoundInstance. * * createjs.Sound.play("MyAudioSprite", {startTime: 1000, duration: 400}); * @@ -1165,17 +3099,8 @@ this.createjs = this.createjs || {}; var s = Sound; - // TODO DEPRECATED - /** - * REMOVED - * Use {{#crossLink "Sound/alternateExtensions:property"}}{{/crossLink}} instead - * @property DELIMITER - * @type {String} - * @default | - * @static - * @deprecated - */ +// Static Properties /** * The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of * instances of the sound are already playing. @@ -1216,7 +3141,6 @@ this.createjs = this.createjs || {}; */ s.INTERRUPT_NONE = "none"; -// The playState in plugins should be implemented with these values. /** * Defines the playState of an instance that is still initializing. * @property PLAY_INITED @@ -1268,7 +3192,7 @@ this.createjs = this.createjs || {}; * can play these types, so modifying this list before a plugin is initialized will allow the plugins to try to * support additional media types. * - * NOTE this does not currently work for {{#crossLink "FlashPlugin"}}{{/crossLink}}. + * NOTE this does not currently work for {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}. * * More details on file formats can be found at <a href="http://en.wikipedia.org/wiki/Audio_file_format" target="_blank">http://en.wikipedia.org/wiki/Audio_file_format</a>.<br /> * A very detailed list of file formats can be found at <a href="http://www.fileinfo.com/filetypes/audio" target="_blank">http://www.fileinfo.com/filetypes/audio</a>. @@ -1304,6 +3228,8 @@ this.createjs = this.createjs || {}; */ s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/; + +// Class Public properties /** * Determines the default behavior for interrupting other currently playing instances with the same source, if the * maximum number of instances of the sound are already playing. Currently the default is {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}} @@ -1326,12 +3252,12 @@ this.createjs = this.createjs || {}; * Note that regardless of which file is loaded, you can call {{#crossLink "Sound/createInstance"}}{{/crossLink}} * and {{#crossLink "Sound/play"}}{{/crossLink}} using the same id or full source path passed for loading. * <h4>Example</h4> - * var manifest = [ + * var sounds = [ * {src:"myPath/mySound.ogg", id:"example"}, * ]; * createjs.Sound.alternateExtensions = ["mp3"]; // now if ogg is not supported, SoundJS will try asset0.mp3 * createjs.Sound.addEventListener("fileload", handleLoad); // call handleLoad when each sound loads - * createjs.Sound.registerManifest(manifest, assetPath); + * createjs.Sound.registerSounds(sounds, assetPath); * // ... * createjs.Sound.play("myPath/mySound.ogg"); // works regardless of what extension is supported. Note calling with ID is a better approach * @@ -1341,15 +3267,6 @@ this.createjs = this.createjs || {}; */ s.alternateExtensions = []; - /** - * Used internally to assign unique IDs to each SoundInstance. - * @property _lastID - * @type {Number} - * @static - * @protected - */ - s._lastID = 0; - /** * The currently active plugin. If this is null, then no plugin could be initialized. If no plugin was specified, * Sound attempts to apply the default plugins: {{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by @@ -1360,6 +3277,8 @@ this.createjs = this.createjs || {}; */ s.activePlugin = null; + +// Class Private properties /** * Determines if the plugins have been registered. If false, the first call to play() will instantiate the default * plugins ({{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}). @@ -1372,6 +3291,15 @@ this.createjs = this.createjs || {}; */ s._pluginsRegistered = false; + /** + * Used internally to assign unique IDs to each AbstractSoundInstance. + * @property _lastID + * @type {Number} + * @static + * @protected + */ + s._lastID = 0; + /** * The master volume value, which affects all sounds. Use {{#crossLink "Sound/getVolume"}}{{/crossLink}} and * {{#crossLink "Sound/setVolume"}}{{/crossLink}} to modify the volume of all audio. @@ -1428,19 +3356,8 @@ this.createjs = this.createjs || {}; */ s._preloadHash = {}; - /** - * An object that stands in for audio that fails to play. This allows developers to continue to call methods - * on the failed instance without having to check if it is valid first. The instance is instantiated once, and - * shared to keep the memory footprint down. - * @property _defaultSoundInstance - * @type {Object} - * @protected - * @static - */ - s._defaultSoundInstance = null; -// mix-ins: - // EventDispatcher methods: +// EventDispatcher methods: s.addEventListener = null; s.removeEventListener = null; s.removeAllEventListeners = null; @@ -1465,32 +3382,19 @@ this.createjs = this.createjs || {}; */ /** - * Used by external plugins to dispatch file load events. - * @method _sendFileLoadEvent - * @param {String} src A sound file has completed loading, and should be dispatched. - * @protected - * @static - * @since 0.4.1 + * This event is fired when a file fails loading internally. This event is fired for each loaded sound, + * so any handler methods should look up the <code>event.src</code> to handle a particular sound. + * @event fileerror + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @param {String} src The source of the sound that was loaded. + * @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null. + * @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined. + * @since 0.6.0 */ - s._sendFileLoadEvent = function (src) { - if (!s._preloadHash[src]) {return;} - for (var i = 0, l = s._preloadHash[src].length; i < l; i++) { - var item = s._preloadHash[src][i]; - s._preloadHash[src][i] = true; - - if (!s.hasEventListener("fileload")) { continue; } - - var event = new createjs.Event("fileload"); - event.src = item.src; - event.id = item.id; - event.data = item.data; - event.sprite = item.sprite; - - s.dispatchEvent(event); - } - }; +// Class Public Methods /** * Get the preload rules to allow Sound to be used as a plugin by <a href="http://preloadjs.com" target="_blank">PreloadJS</a>. * Any load calls that have the matching type or extension will fire the callback method, and use the resulting @@ -1513,6 +3417,60 @@ this.createjs = this.createjs || {}; }; }; + /** + * Used to dispatch fileload events from internal loading. + * @method _handleLoadComplete + * @param event A loader event. + * @protected + * @static + * @since 0.6.0 + */ + s._handleLoadComplete = function(event) { + var src = event.target.getItem().src; + if (!s._preloadHash[src]) {return;} + + for (var i = 0, l = s._preloadHash[src].length; i < l; i++) { + var item = s._preloadHash[src][i]; + s._preloadHash[src][i] = true; + + if (!s.hasEventListener("fileload")) { continue; } + + var event = new createjs.Event("fileload"); + event.src = item.src; + event.id = item.id; + event.data = item.data; + event.sprite = item.sprite; + + s.dispatchEvent(event); + } + }; + + /** + * Used to dispatch error events from internal preloading. + * @param event + * @protected + * @since 0.6.0 + */ + s._handleLoadError = function(event) { + var src = event.target.getItem().src; + if (!s._preloadHash[src]) {return;} + + for (var i = 0, l = s._preloadHash[src].length; i < l; i++) { + var item = s._preloadHash[src][i]; + s._preloadHash[src][i] = false; + + if (!s.hasEventListener("fileerror")) { continue; } + + var event = new createjs.Event("fileerror"); + event.src = item.src; + event.id = item.id; + event.data = item.data; + event.sprite = item.sprite; + + s.dispatchEvent(event); + } + }; + /** * Used by {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} to register a Sound plugin. * @@ -1535,8 +3493,8 @@ this.createjs = this.createjs || {}; * Register a list of Sound plugins, in order of precedence. To register a single plugin, pass a single element in the array. * * <h4>Example</h4> - * createjs.FlashPlugin.swfPath = "../src/SoundJS/"; - * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashPlugin]); + * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/"; + * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]); * * @method registerPlugins * @param {Array} plugins An array of plugins classes to install. @@ -1579,8 +3537,8 @@ this.createjs = this.createjs || {}; * This example sets up a Flash fallback, but only if there is no plugin specified yet. * * if (!createjs.Sound.isReady()) { - * createjs.FlashPlugin.swfPath = "../src/SoundJS/"; - * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashPlugin]); + * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/"; + * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]); * } * * @method isReady @@ -1676,7 +3634,7 @@ this.createjs = this.createjs || {}; * Returns true if the source is already loaded. * @static * @private - * @since 0.5.3 + * @since 0.6.0 */ s._registerSound = function (src, id, data) { @@ -1716,8 +3674,8 @@ this.createjs = this.createjs || {}; details.data.channels = numChannels || SoundChannel.maxPerChannel(); } - details.tag = loader.tag; - if (loader.completeHandler) {details.completeHandler = loader.completeHandler;} + details.loader = loader; + if (loader.onload) {details.completeHandler = loader.onload;} // used by preloadJS if (loader.type) {details.type = loader.type;} return details; @@ -1768,7 +3726,10 @@ this.createjs = this.createjs || {}; s._preloadHash[details.src].push({src:src, id:id, data:details.data}); if (s._preloadHash[details.src].length == 1) { // OJR note this will disallow reloading a sound if loading fails or the source changes - s.activePlugin.preload(details.src, details.tag); + var loader = details.loader; + loader.on("complete", createjs.proxy(this._handleLoadComplete, this)); + loader.on("error", createjs.proxy(this._handleLoadError, this)); + s.activePlugin.preload(details.loader); } else { if (s._preloadHash[details.src][0] == true) {return true;} } @@ -1777,22 +3738,52 @@ this.createjs = this.createjs || {}; }; /** - * Register a manifest of audio files for loading and future playback in Sound. It is recommended to register all + * Register an array of audio files for loading and future playback in Sound. It is recommended to register all * sounds that need to be played back in order to properly prepare and preload them. Sound does internal preloading * when required. * * <h4>Example</h4> - * var manifest = [ + * var sounds = [ * {src:"asset0.ogg", id:"example"}, * {src:"asset1.ogg", id:"1", data:6}, * {src:"asset2.mp3", id:"works"} * ]; * createjs.Sound.alternateExtensions = ["mp3"]; // if the passed extension is not supported, try this extension * createjs.Sound.addEventListener("fileload", handleLoad); // call handleLoad when each sound loads - * createjs.Sound.registerManifest(manifest, assetPath); + * createjs.Sound.registerSounds(sounds, assetPath); + * + * @method registerSounds + * @param {Array} sounds An array of objects to load. Objects are expected to be in the format needed for + * {{#crossLink "Sound/registerSound"}}{{/crossLink}}: <code>{src:srcURI, id:ID, data:Data}</code> + * with "id" and "data" being optional. You can also set an optional path property that will be prepended to the src of each object. + * @param {string} basePath Set a path that will be prepended to each src when loading. When creating, playing, or removing + * audio that was loaded with a basePath by src, the basePath must be included. + * @return {Object} An array of objects with the modified values that were passed in, which defines each sound. + * Like registerSound, it will return false for any values when the source cannot be parsed or if no plugins can be initialized. + * Also, it will return true for any values when the source is already loaded. + * @static + * @since 0.6.0 + */ + s.registerSounds = function (sounds, basePath) { + var returnValues = []; + if (sounds.path) { + if (!basePath) { + basePath = sounds.path; + } else { + basePath = basePath + sounds.path; + } + } + for (var i = 0, l = sounds.length; i < l; i++) { + returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath); + } + return returnValues; + }; + + /** + * Deprecated. Please use {{#crossLink "Sound/registerSounds"}}{{/crossLink} instead. * * @method registerManifest - * @param {Array} manifest An array of objects to load. Objects are expected to be in the format needed for + * @param {Array} sounds An array of objects to load. Objects are expected to be in the format needed for * {{#crossLink "Sound/registerSound"}}{{/crossLink}}: <code>{src:srcURI, id:ID, data:Data}</code> * with "id" and "data" being optional. * @param {string} basePath Set a path that will be prepended to each src when loading. When creating, playing, or removing @@ -1800,20 +3791,21 @@ this.createjs = this.createjs || {}; * @return {Object} An array of objects with the modified values that were passed in, which defines each sound. * Like registerSound, it will return false for any values when the source cannot be parsed or if no plugins can be initialized. * Also, it will return true for any values when the source is already loaded. - * @static * @since 0.4.0 - */ - s.registerManifest = function (manifest, basePath) { - var returnValues = []; - for (var i = 0, l = manifest.length; i < l; i++) { - returnValues[i] = createjs.Sound.registerSound(manifest[i].src, manifest[i].id, manifest[i].data, basePath); - } - return returnValues; + * @depreacted + */ + s.registerManifest = function(manifest, basePath) { + try { + console.log("createjs.Sound.registerManifest is deprecated, please use createjs.Sound.registerSounds.") + } catch (error) { + + }; + return this.registerSounds(manifest, basePath); }; /** * Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or - * {{#crossLink "Sound/registerManifest"}}{{/crossLink}}. + * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}. * <br />Note this will stop playback on active instances playing this sound before deleting them. * <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here. * @@ -1856,18 +3848,46 @@ this.createjs = this.createjs || {}; }; /** - * Remove a manifest of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or - * {{#crossLink "Sound/registerManifest"}}{{/crossLink}}. + * Remove an array of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or + * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}. * <br />Note this will stop playback on active instances playing this audio before deleting them. * <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here. * * <h4>Example</h4> - * var manifest = [ + * var sounds = [ * {src:"asset0.ogg", id:"example"}, * {src:"asset1.ogg", id:"1", data:6}, * {src:"asset2.mp3", id:"works"} * ]; - * createjs.Sound.removeManifest(manifest, assetPath); + * createjs.Sound.removeSounds(sounds, assetPath); + * + * @method removeSounds + * @param {Array} sounds An array of objects to remove. Objects are expected to be in the format needed for + * {{#crossLink "Sound/removeSound"}}{{/crossLink}}: <code>{srcOrID:srcURIorID}</code>. + * You can also set an optional path property that will be prepended to the src of each object. + * @param {string} basePath Set a path that will be prepended to each src when removing. + * @return {Object} An array of Boolean values representing if the sounds with the same array index were + * successfully removed. + * @static + * @since 0.4.1 + */ + s.removeSounds = function (sounds, basePath) { + var returnValues = []; + if (sounds.path) { + if (!basePath) { + basePath = sounds.path; + } else { + basePath = basePath + sounds.path; + } + } + for (var i = 0, l = sounds.length; i < l; i++) { + returnValues[i] = createjs.Sound.removeSound(sounds[i].src, basePath); + } + return returnValues; + }; + + /** + * Deprecated. Please use {{#crossLink "Sound/removeSounds"}}{{/crossLink}} instead. * * @method removeManifest * @param {Array} manifest An array of objects to remove. Objects are expected to be in the format needed for @@ -1877,18 +3897,20 @@ this.createjs = this.createjs || {}; * successfully removed. * @static * @since 0.4.1 + * @deprecated */ s.removeManifest = function (manifest, basePath) { - var returnValues = []; - for (var i = 0, l = manifest.length; i < l; i++) { - returnValues[i] = createjs.Sound.removeSound(manifest[i].src, basePath); - } - return returnValues; + try { + console.log("createjs.Sound.removeManifest is deprecated, please use createjs.Sound.removeSounds."); + } catch (error) { + + }; + return s.removeSounds(manifest, basePath); }; /** * Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or - * {{#crossLink "Sound/registerManifest"}}{{/crossLink}}. + * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}. * <br />Note this will stop playback on all active sound instances before deleting them. * * <h4>Example</h4> @@ -1932,7 +3954,7 @@ this.createjs = this.createjs || {}; }; /** - * Parse the path of a sound, usually from a manifest item. alternate extensions will be attempted in order if the + * Parse the path of a sound. alternate extensions will be attempted in order if the * current extension is not supported * @method _parsePath * @param {String} value The path to an audio source. @@ -1964,18 +3986,18 @@ this.createjs = this.createjs || {}; Static API. --------------- */ /** - * Play a sound and get a {{#crossLink "SoundInstance"}}{{/crossLink}} to control. If the sound fails to play, a - * SoundInstance will still be returned, and have a playState of {{#crossLink "Sound/PLAY_FAILED:property"}}{{/crossLink}}. - * Note that even on sounds with failed playback, you may still be able to call SoundInstance {{#crossLink "SoundInstance/play"}}{{/crossLink}}, + * Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to play, a + * AbstractSoundInstance will still be returned, and have a playState of {{#crossLink "Sound/PLAY_FAILED:property"}}{{/crossLink}}. + * Note that even on sounds with failed playback, you may still be able to call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}, * since the failure could be due to lack of available channels. If the src does not have a supported extension or - * if there is no available plugin, a default SoundInstance will be returned which will not play any audio, but will not generate errors. + * if there is no available plugin, a default AbstractSoundInstance will be returned which will not play any audio, but will not generate errors. * * <h4>Example</h4> * createjs.Sound.addEventListener("fileload", handleLoad); * createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3); * function handleLoad(event) { * createjs.Sound.play("myID"); - * // we can pass in options we want to set inside of an object, and store off SoundInstance for controlling + * // we can pass in options we want to set inside of an object, and store off AbstractSoundInstance for controlling * var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1}); * // alternately, we can pass full source path and specify each argument individually * var myInstance = createjs.Sound.play("myAudioPath/mySound.mp3", createjs.Sound.INTERRUPT_ANY, 0, 0, -1, 1, 0); @@ -2001,7 +4023,7 @@ this.createjs = this.createjs || {}; * @param {Number} [pan=0] The left-right pan of the sound (if supported), between -1 (left) and 1 (right). * @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds. * @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds. - * @return {SoundInstance} A {{#crossLink "SoundInstance"}}{{/crossLink}} that can be controlled after it is created. + * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created. * @static */ s.play = function (src, interrupt, delay, offset, loop, volume, pan, startTime, duration) { @@ -2018,13 +4040,13 @@ this.createjs = this.createjs || {}; } var instance = s.createInstance(src, startTime, duration); var ok = s._playInstance(instance, interrupt, delay, offset, loop, volume, pan); - if (!ok) {instance.playFailed();} + if (!ok) {instance._playFailed();} return instance; }; /** - * Creates a {{#crossLink "SoundInstance"}}{{/crossLink}} using the passed in src. If the src does not have a - * supported extension or if there is no available plugin, a default SoundInstance will be returned that can be + * Creates a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} using the passed in src. If the src does not have a + * supported extension or if there is no available plugin, a default AbstractSoundInstance will be returned that can be * called safely but does nothing. * * <h4>Example</h4> @@ -2044,12 +4066,12 @@ this.createjs = this.createjs || {}; * @param {String} src The src or ID of the audio. * @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds. * @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds. - * @return {SoundInstance} A {{#crossLink "SoundInstance"}}{{/crossLink}} that can be controlled after it is created. - * Unsupported extensions will return the default SoundInstance. + * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created. + * Unsupported extensions will return the default AbstractSoundInstance. * @since 0.4.0 */ s.createInstance = function (src, startTime, duration) { - if (!s.initializeDefaultPlugins()) {return s._defaultSoundInstance;} + if (!s.initializeDefaultPlugins()) {return new createjs.DefaultSoundInstance(src, startTime, duration);} src = s._getSrcById(src); @@ -2061,7 +4083,7 @@ this.createjs = this.createjs || {}; if (startTime == null) {startTime = src.startTime;} instance = s.activePlugin.create(details.src, startTime, duration || src.duration); } else { - instance = Sound._defaultSoundInstance; + instance = new createjs.DefaultSoundInstance(src, startTime, duration);; } instance.uniqueId = s._lastID++; @@ -2072,7 +4094,7 @@ this.createjs = this.createjs || {}; /** * Set the master volume of Sound. The master volume is multiplied against each sound's individual volume. For * example, if master volume is 0.5 and a sound's volume is 0.5, the resulting volume is 0.25. To set individual - * sound volume, use SoundInstance {{#crossLink "SoundInstance/setVolume"}}{{/crossLink}} instead. + * sound volume, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/setVolume"}}{{/crossLink}} instead. * * <h4>Example</h4> * createjs.Sound.setVolume(0.5); @@ -2095,7 +4117,7 @@ this.createjs = this.createjs || {}; /** * Get the master volume of Sound. The master volume is multiplied against each sound's individual volume. - * To get individual sound volume, use SoundInstance {{#crossLink "SoundInstance/volume:property"}}{{/crossLink}} instead. + * To get individual sound volume, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} instead. * * <h4>Example</h4> * var masterVolume = createjs.Sound.getVolume(); @@ -2111,7 +4133,7 @@ this.createjs = this.createjs || {}; /** * Mute/Unmute all audio. Note that muted audio still plays at 0 volume. This global mute value is maintained * separately and when set will override, but not change the mute property of individual instances. To mute an individual - * instance, use SoundInstance {{#crossLink "SoundInstance/setMute"}}{{/crossLink}} instead. + * instance, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/setMute"}}{{/crossLink}} instead. * * <h4>Example</h4> * createjs.Sound.setMute(true); @@ -2136,8 +4158,8 @@ this.createjs = this.createjs || {}; }; /** - * Returns the global mute value. To get the mute value of an individual instance, use SoundInstance - * {{#crossLink "SoundInstance/getMute"}}{{/crossLink}} instead. + * Returns the global mute value. To get the mute value of an individual instance, use AbstractSoundInstance + * {{#crossLink "AbstractSoundInstance/getMute"}}{{/crossLink}} instead. * * <h4>Example</h4> * var muted = createjs.Sound.getMute(); @@ -2153,7 +4175,7 @@ this.createjs = this.createjs || {}; /** * Stop all audio (global stop). Stopped audio is reset, and not paused. To play audio that has been stopped, - * call SoundInstance {{#crossLink "SoundInstance/play"}}{{/crossLink}}. + * call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}. * * <h4>Example</h4> * createjs.Sound.stop(); @@ -2176,7 +4198,7 @@ this.createjs = this.createjs || {}; * Play an instance. This is called by the static API, as well as from plugins. This allows the core class to * control delays. * @method _playInstance - * @param {SoundInstance} instance The {{#crossLink "SoundInstance"}}{{/crossLink}} to start playing. + * @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing. * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source, * if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code> * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior"}}{{/crossLink}}. @@ -2220,7 +4242,7 @@ this.createjs = this.createjs || {}; var delayTimeoutId = setTimeout(function () { s._beginPlaying(instance, interrupt, offset, loop, volume, pan); }, delay); - instance._delayTimeoutId = delayTimeoutId; + instance.delayTimeoutId = delayTimeoutId; } this._instances.push(instance); @@ -2231,7 +4253,7 @@ this.createjs = this.createjs || {}; /** * Begin playback. This is called immediately or after delay by {{#crossLink "Sound/playInstance"}}{{/crossLink}}. * @method _beginPlaying - * @param {SoundInstance} instance A {{#crossLink "SoundInstance"}}{{/crossLink}} to begin playback. + * @param {AbstractSoundInstance} instance A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to begin playback. * @param {String} [interrupt=none] How this sound interrupts other instances with the same source. Defaults to * {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}. Interrupts are defined as <code>INTERRUPT_TYPE</code> * constants on Sound. @@ -2276,7 +4298,7 @@ this.createjs = this.createjs || {}; * Sound management. It will be added again, if the sound re-plays. Note that this method is called from the * instances themselves. * @method _playFinished - * @param {SoundInstance} instance The instance that finished playback. + * @param {AbstractSoundInstance} instance The instance that finished playback. * @protected * @static */ @@ -2289,7 +4311,7 @@ this.createjs = this.createjs || {}; createjs.Sound = Sound; /** - * An internal class that manages the number of active {{#crossLink "SoundInstance"}}{{/crossLink}} instances for + * An internal class that manages the number of active {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances for * each sound type. This method is only used internally by the {{#crossLink "Sound"}}{{/crossLink}} class. * * The number of sounds is artificially limited by Sound in order to prevent over-saturation of a @@ -2363,7 +4385,7 @@ this.createjs = this.createjs || {}; /** * Add an instance to a sound channel. * #method add - * @param {SoundInstance} instance The instance to add to the channel + * @param {AbstractSoundInstance} instance The instance to add to the channel * @param {String} interrupt The interrupt value to use. Please see the {{#crossLink "Sound/play"}}{{/crossLink}} * for details on interrupt modes. * @return {Boolean} The success of the method call. If the channel is full, it will return false. @@ -2377,7 +4399,7 @@ this.createjs = this.createjs || {}; /** * Remove an instance from the channel. * #method remove - * @param {SoundInstance} instance The instance to remove from the channel + * @param {AbstractSoundInstance} instance The instance to remove from the channel * @return The success of the method call. If there is no channel, it will return false. * @static */ @@ -2456,7 +4478,7 @@ this.createjs = this.createjs || {}; * Get an instance by index. * #method get * @param {Number} index The index to return. - * @return {SoundInstance} The SoundInstance at a specific instance. + * @return {AbstractSoundInstance} The AbstractSoundInstance at a specific instance. */ p._get = function (index) { return this._instances[index]; @@ -2465,7 +4487,7 @@ this.createjs = this.createjs || {}; /** * Add a new instance to the channel. * #method add - * @param {SoundInstance} instance The instance to add. + * @param {AbstractSoundInstance} instance The instance to add. * @return {Boolean} The success of the method call. If the channel is full, it will return false. */ p._add = function (instance, interrupt) { @@ -2478,7 +4500,7 @@ this.createjs = this.createjs || {}; /** * Remove an instance from the channel, either when it has finished playing, or it has been interrupted. * #method remove - * @param {SoundInstance} instance The instance to remove + * @param {AbstractSoundInstance} instance The instance to remove * @return {Boolean} The success of the remove call. If the instance is not found in this channel, it will * return false. */ @@ -2505,9 +4527,9 @@ this.createjs = this.createjs || {}; * Get an available slot depending on interrupt value and if slots are available. * #method getSlot * @param {String} interrupt The interrupt value to use. - * @param {SoundInstance} instance The sound instance that will go in the channel if successful. + * @param {AbstractSoundInstance} instance The sound instance that will go in the channel if successful. * @return {Boolean} Determines if there is an available slot. Depending on the interrupt mode, if there are no slots, - * an existing SoundInstance may be interrupted. If there are no slots, this method returns false. + * an existing AbstractSoundInstance may be interrupted. If there are no slots, this method returns false. */ p._getSlot = function (interrupt, instance) { var target, replacement; @@ -2558,95 +4580,1637 @@ this.createjs = this.createjs || {}; p.toString = function () { return "[Sound SoundChannel]"; }; - // do not add SoundChannel to namespace +}()); - // This is a dummy sound instance, which allows Sound to return something so developers don't need to check nulls. - function SoundInstance() { - this.isDefault = true; - this.addEventListener = this.on = this.off = this.removeEventListener = this.removeAllEventListeners = this.dispatchEvent = this.hasEventListener = this._listeners = this._interrupt = this._playFailed = this.pause = this.resume = this.play = this._beginPlaying = this._cleanUp = this.stop = this.setMasterVolume = this.setVolume = this.mute = this.setMute = this.getMute = this.setPan = this.getPosition = this.setPosition = this.playFailed = function () { +//############################################################################## +// AbstractSoundInstance.js +//############################################################################## + +this.createjs = this.createjs || {}; + +/** + * A AbstractSoundInstance is created when any calls to the Sound API method {{#crossLink "Sound/play"}}{{/crossLink}} or + * {{#crossLink "Sound/createInstance"}}{{/crossLink}} are made. The AbstractSoundInstance is returned by the active plugin + * for control by the user. + * + * <h4>Example</h4> + * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3"); + * + * A number of additional parameters provide a quick way to determine how a sound is played. Please see the Sound + * API method {{#crossLink "Sound/play"}}{{/crossLink}} for a list of arguments. + * + * Once a AbstractSoundInstance is created, a reference can be stored that can be used to control the audio directly through + * the AbstractSoundInstance. If the reference is not stored, the AbstractSoundInstance will play out its audio (and any loops), and + * is then de-referenced from the {{#crossLink "Sound"}}{{/crossLink}} class so that it can be cleaned up. If audio + * playback has completed, a simple call to the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}} instance method + * will rebuild the references the Sound class need to control it. + * + * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3", {loop:2}); + * myInstance.addEventListener("loop", handleLoop); + * function handleLoop(event) { + * myInstance.volume = myInstance.volume * 0.5; + * } + * + * Events are dispatched from the instance to notify when the sound has completed, looped, or when playback fails + * + * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3"); + * myInstance.addEventListener("complete", handleComplete); + * myInstance.addEventListener("loop", handleLoop); + * myInstance.addEventListener("failed", handleFailed); + * + * + * @class AbstractSoundInstance + * @param {String} src The path to and file name of the sound. + * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds. + * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds. + * @param {Object} playbackResource Any resource needed by plugin to support audio playback. + * @extends EventDispatcher + * @constructor + */ + +(function () { + "use strict"; + + +// Constructor: + var AbstractSoundInstance = function (src, startTime, duration, playbackResource) { + this.EventDispatcher_constructor(); + + + // public properties: + /** + * The source of the sound. + * @property src + * @type {String} + * @default null + */ + this.src = src; + + /** + * The unique ID of the instance. This is set by {{#crossLink "Sound"}}{{/crossLink}}. + * @property uniqueId + * @type {String} | Number + * @default -1 + */ + this.uniqueId = -1; + + /** + * The play state of the sound. Play states are defined as constants on {{#crossLink "Sound"}}{{/crossLink}}. + * @property playState + * @type {String} + * @default null + */ + this.playState = null; + + /** + * A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this AbstractSoundInstance is played with a delay. + * This allows AbstractSoundInstance to remove the delay if stop, pause, or cleanup are called before playback begins. + * @property delayTimeoutId + * @type {timeoutVariable} + * @default null + * @protected + * @since 0.4.0 + */ + this.delayTimeoutId = null; + // TODO consider moving delay into AbstractSoundInstance so it can be handled by plugins + + + // private properties + /** + * Audio sprite property used to determine the starting offset. + * @type {Number} + * @default null + * @protected + */ + this._startTime = Math.max(0, startTime || 0); + //TODO add a getter / setter for startTime? + + + // Getter / Setter Properties + // OJR TODO find original reason that we didn't use defined functions. I think it was performance related + /** + * The volume of the sound, between 0 and 1. + * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower and Opera versions 11.50 or lower, + * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setVolume"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getVolume"}}{{/crossLink}}. + * + * The actual output volume of a sound can be calculated using: + * <code>myInstance.volume * createjs.Sound.getVolume();</code> + * + * @property volume + * @type {Number} + * @default 1 + */ + this._volume = 1; + if (createjs.definePropertySupported) { + Object.defineProperty(this, "volume", { + get: this.getVolume, + set: this.setVolume + }); + } + + /** + * The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio. + * + * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower, + * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setPan"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getPan"}}{{/crossLink}}. + * <br />Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio. + * + * @property pan + * @type {Number} + * @default 0 + */ + this._pan = 0; + if (createjs.definePropertySupported) { + Object.defineProperty(this, "pan", { + get: this.getPan, + set: this.setPan + }); + } + + /** + * The length of the audio clip, in milliseconds. + * + * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower, + * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setDuration"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getDuration"}}{{/crossLink}}. + * + * @property duration + * @type {Number} + * @default 0 + * @since 0.6.0 + */ + this._duration = Math.max(0, duration || 0); + if (createjs.definePropertySupported) { + Object.defineProperty(this, "duration", { + get: this.getDuration, + set: this.setDuration + }); + } + + /** + * Object that holds plugin specific resource need for audio playback. + * This is set internally by the plugin. For example, WebAudioPlugin will set an array buffer, + * HTMLAudioPlugin will set a tag, FlashAudioPlugin will set a flash reference. + * + * @property playbackResource + * @type {Object} + * @default null + */ + this._playbackResource = null; + if (createjs.definePropertySupported) { + Object.defineProperty(this, "playbackResource", { + get: this.getPlaybackResource, + set: this.setPlaybackResource + }); + } + if(playbackResource !== false && playbackResource !== true) { this.setPlaybackResource(playbackResource); } + + /** + * The position of the playhead in milliseconds. This can be set while a sound is playing, paused, or stopped. + * + * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower, + * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setPosition"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getPosition"}}{{/crossLink}}. + * + * @property position + * @type {Number} + * @default 0 + * @since 0.6.0 + */ + this._position = 0; + if (createjs.definePropertySupported) { + Object.defineProperty(this, "position", { + get: this.getPosition, + set: this.setPosition + }); + } + + /** + * The number of play loops remaining. Negative values will loop infinitely. + * + * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower, + * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setLoop"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getLoop"}}{{/crossLink}}. + * + * @property loop + * @type {Number} + * @default 0 + * @public + * @since 0.6.0 + */ + this._loop = 0; + if (createjs.definePropertySupported) { + Object.defineProperty(this, "loop", { + get: this.getLoop, + set: this.setLoop + }); + } + + /** + * Determines if the audio is currently muted. + * + * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower, + * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setMute"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getMute"}}{{/crossLink}}. + * + * @property muted + * @type {Boolean} + * @default false + * @since 0.6.0 + */ + this._muted = false; + if (createjs.definePropertySupported) { + Object.defineProperty(this, "muted", { + get: this.getMuted, + set: this.setMuted + }); + } + + /** + * Tells you if the audio is currently paused. + * + * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower, + * and Internet Explorer 8 or lower. + * Use {{#crossLink "AbstractSoundInstance/pause:method"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/resume:method"}}{{/crossLink}} to set. + * + * @property paused + * @type {Boolean} + */ + this._paused = false; + if (createjs.definePropertySupported) { + Object.defineProperty(this, "paused", { + get: this.getPaused, + set: this.setPaused + }); + } + + + // Events + /** + * The event that is fired when playback has started successfully. + * @event succeeded + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @since 0.4.0 + */ + + /** + * The event that is fired when playback is interrupted. This happens when another sound with the same + * src property is played using an interrupt value that causes this instance to stop playing. + * @event interrupted + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @since 0.4.0 + */ + + /** + * The event that is fired when playback has failed. This happens when there are too many channels with the same + * src property already playing (and the interrupt value doesn't cause an interrupt of another instance), or + * the sound could not be played, perhaps due to a 404 error. + * @event failed + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @since 0.4.0 + */ + + /** + * The event that is fired when a sound has completed playing but has loops remaining. + * @event loop + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @since 0.4.0 + */ + + /** + * The event that is fired when playback completes. This means that the sound has finished playing in its + * entirety, including its loop iterations. + * @event complete + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @since 0.4.0 + */ + }; + + var p = createjs.extend(AbstractSoundInstance, createjs.EventDispatcher); + + +// Public Methods: + /** + * Play an instance. This method is intended to be called on SoundInstances that already exist (created + * with the Sound API {{#crossLink "Sound/createInstance"}}{{/crossLink}} or {{#crossLink "Sound/play"}}{{/crossLink}}). + * + * <h4>Example</h4> + * var myInstance = createjs.Sound.createInstance(mySrc); + * myInstance.play({offset:1, loop:2, pan:0.5}); // options as object properties + * myInstance.play(createjs.Sound.INTERRUPT_ANY); // options as parameters + * + * Note that if this sound is already playing, this call will do nothing. + * + * @method play + * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source, + * if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code> + * constants on the Sound class, with the default defined by Sound {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}. + * <br /><strong>OR</strong><br /> + * This parameter can be an object that contains any or all optional properties by name, including: interrupt, + * delay, offset, loop, volume, and pan (see the above code sample). + * @param {Number} [delay=0] The delay in milliseconds before the sound starts + * @param {Number} [offset=0] How far into the sound to begin playback, in milliseconds. + * @param {Number} [loop=0] The number of times to loop the audio. Use -1 for infinite loops. + * @param {Number} [volume=1] The volume of the sound, between 0 and 1. + * @param {Number} [pan=0] The pan of the sound between -1 (left) and 1 (right). Note that pan is not supported + * for HTML Audio. + * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls. + */ + p.play = function (interrupt, delay, offset, loop, volume, pan) { + if (this.playState == createjs.Sound.PLAY_SUCCEEDED) { + if (interrupt instanceof Object) { + offset = interrupt.offset; + loop = interrupt.loop; + volume = interrupt.volume; + pan = interrupt.pan; + } + if (offset != null) { this.setPosition(offset) } + if (loop != null) { this.setLoop(loop); } + if (volume != null) { this.setVolume(volume); } + if (pan != null) { this.setPan(pan); } + if (this._paused) { this.setPaused(false); } + return; + } + this._cleanUp(); + createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan); // make this an event dispatch?? + return this; + }; + + /** + * Deprecated, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} instead. + * + * @method pause + * @return {Boolean} If the pause call succeeds. This will return false if the sound isn't currently playing. + * @deprecated + */ + p.pause = function () { + if (this._paused || this.playState != createjs.Sound.PLAY_SUCCEEDED) {return false;} + this.setPaused(true); + return true; + }; + + /** + * Deprecated, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} instead. + * + * @method resume + * @return {Boolean} If the resume call succeeds. This will return false if called on a sound that is not paused. + * @deprecated + */ + p.resume = function () { + if (!this._paused) {return false;} + this.setPaused(false); + return true; + }; + + /** + * Stop playback of the instance. Stopped sounds will reset their position to 0, and calls to {{#crossLink "AbstractSoundInstance/resume"}}{{/crossLink}} + * will fail. To start playback again, call {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}. + * + * <h4>Example</h4> + * + * myInstance.stop(); + * + * @method stop + * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls. + */ + p.stop = function () { + this._position = 0; + this._paused = false; + this._handleStop(); + this._cleanUp(); + this.playState = createjs.Sound.PLAY_FINISHED; + return this; + }; + + /** + * Remove all external references and resources from AbstractSoundInstance. Note this is irreversible and AbstractSoundInstance will no longer work + * @method destroy + * @since 0.6.0 + */ + p.destroy = function() { + this._cleanUp(); + this.src = null; + this.playbackResource = null; + + this.removeAllEventListeners(); + }; + + p.toString = function () { + return "[AbstractSoundInstance]"; + }; + + +// get/set methods that allow support for IE8 + /** + * NOTE {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} can be accessed directly as a property, + * and getPaused remains to allow support for IE8 with FlashAudioPlugin. + * + * Returns true if the instance is currently paused. + * + * @method getPaused + * @returns {boolean} If the instance is currently paused + * @since 0.6.0 + */ + p.getPaused = function() { + return this._paused; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} can be accessed directly as a property, + * setPaused remains to allow support for IE8 with FlashAudioPlugin. + * + * Pause or resume the instance. Note you can also resume playback with {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}. + * + * @param {boolean} value + * @since 0.6.0 + * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls. + */ + p.setPaused = function (value) { + if ((value !== true && value !== false) || this._paused == value) {return;} + if (value == true && this.playState != createjs.Sound.PLAY_SUCCEEDED) {return;} + this._paused = value; + if(value) { + this._pause(); + } else { + this._resume(); + } + clearTimeout(this.delayTimeoutId); + return this; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} can be accessed directly as a property, + * setVolume remains to allow support for IE8 with FlashAudioPlugin. + * + * Set the volume of the instance. + * + * <h4>Example</h4> + * myInstance.setVolume(0.5); + * + * Note that the master volume set using the Sound API method {{#crossLink "Sound/setVolume"}}{{/crossLink}} + * will be applied to the instance volume. + * + * @method setVolume + * @param value The volume to set, between 0 and 1. + * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls. + */ + p.setVolume = function (value) { + if (value == this._volume) { return this; } + this._volume = Math.max(0, Math.min(1, value)); + if (!this._muted) { + this._updateVolume(); + } + return this; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} can be accessed directly as a property, + * getVolume remains to allow support for IE8 with FlashAudioPlugin. + * + * Get the volume of the instance. The actual output volume of a sound can be calculated using: + * <code>myInstance.getVolume() * createjs.Sound.getVolume();</code> + * + * @method getVolume + * @return The current volume of the sound instance. + */ + p.getVolume = function () { + return this._volume; + }; + + /** + * Deprecated, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead. + * + * @method setMute + * @param {Boolean} value If the sound should be muted. + * @return {Boolean} If the mute call succeeds. + * @deprecated + */ + p.setMute = function (value) { + this.setMuted(value); + }; + + /** + * Deprecated, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead. + * + * @method getMute + * @return {Boolean} If the sound is muted. + * @deprecated + */ + p.getMute = function () { + return this._muted; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} can be accessed directly as a property, + * setMuted exists to allow support for IE8 with FlashAudioPlugin. + * + * Mute and unmute the sound. Muted sounds will still play at 0 volume. Note that an unmuted sound may still be + * silent depending on {{#crossLink "Sound"}}{{/crossLink}} volume, instance volume, and Sound muted. + * + * <h4>Example</h4> + * myInstance.setMuted(true); + * + * @method setMute + * @param {Boolean} value If the sound should be muted. + * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls. + * @since 0.6.0 + */ + p.setMuted = function (value) { + if (value !== true && value !== false) {return;} + this._muted = value; + this._updateVolume(); + return this; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} can be accessed directly as a property, + * getMuted remains to allow support for IE8 with FlashAudioPlugin. + * + * Get the mute value of the instance. + * + * <h4>Example</h4> + * var isMuted = myInstance.getMuted(); + * + * @method getMute + * @return {Boolean} If the sound is muted. + * @since 0.6.0 + */ + p.getMuted = function () { + return this._muted; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} can be accessed directly as a property, + * getPan remains to allow support for IE8 with FlashAudioPlugin. + * + * Set the left(-1)/right(+1) pan of the instance. Note that {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}} does not + * support panning, and only simple left/right panning has been implemented for {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. + * The default pan value is 0 (center). + * + * <h4>Example</h4> + * + * myInstance.setPan(-1); // to the left! + * + * @method setPan + * @param {Number} value The pan value, between -1 (left) and 1 (right). + * @return {AbstractSoundInstance} Returns reference to itself for chaining calls + */ + p.setPan = function (value) { + if(value == this._pan) { return this; } + this._pan = Math.max(-1, Math.min(1, value)); + this._updatePan(); + return this; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} can be accessed directly as a property, + * getPan remains to allow support for IE8 with FlashAudioPlugin. + * + * Get the left/right pan of the instance. Note in WebAudioPlugin this only gives us the "x" value of what is + * actually 3D audio. + * + * <h4>Example</h4> + * + * var myPan = myInstance.getPan(); + * + * @method getPan + * @return {Number} The value of the pan, between -1 (left) and 1 (right). + */ + p.getPan = function () { + return this._pan; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} can be accessed directly as a property, + * getPosition remains to allow support for IE8 with FlashAudioPlugin. + * + * Get the position of the playhead of the instance in milliseconds. + * + * <h4>Example</h4> + * var currentOffset = myInstance.getPosition(); + * + * @method getPosition + * @return {Number} The position of the playhead in the sound, in milliseconds. + */ + p.getPosition = function () { + if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) { + return this._calculateCurrentPosition(); // sets this._position + } + return this._position; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} can be accessed directly as a property, + * setPosition remains to allow support for IE8 with FlashAudioPlugin. + * + * Set the position of the playhead in the instance. This can be set while a sound is playing, paused, or + * stopped. + * + * <h4>Example</h4> + * myInstance.setPosition(myInstance.getDuration()/2); // set audio to its halfway point. + * + * @method setPosition + * @param {Number} value The position to place the playhead, in milliseconds. + * @return {AbstractSoundInstance} Returns reference to itself for chaining calls + */ + p.setPosition = function (value) { + this._position = Math.max(0, value); + if (this.playState == createjs.Sound.PLAY_SUCCEEDED) { + this._updatePosition(); + } + return this; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} can be accessed directly as a property, + * getDuration exists to allow support for IE8 with FlashAudioPlugin. + * + * Get the duration of the instance, in milliseconds. + * Note a sound needs to be loaded before it will have duration, unless it was set manually to create an audio sprite. + * + * <h4>Example</h4> + * var soundDur = myInstance.getDuration(); + * + * @method getDuration + * @return {Number} The duration of the sound instance in milliseconds. + */ + p.getDuration = function () { + return this._duration; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} can be accessed directly as a property, + * setDuration exists to allow support for IE8 with FlashAudioPlugin. + * + * Set the duration of the audio. Generally this is not called, but it can be used to create an audio sprite out of an existing AbstractSoundInstance. + * + * @method setDuration + * @param {number} value The new duration time in milli seconds. + * @return {AbstractSoundInstance} Returns reference to itself for chaining calls + * @since 0.6.0 + */ + p.setDuration = function (value) { + if (value == this._duration) { return this; } + this._duration = Math.max(0, value || 0); + this._updateDuration(); + return this; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} can be accessed directly as a property, + * setPlaybackResource exists to allow support for IE8 with FlashAudioPlugin. + * + * An object containing any resources needed for audio playback, set by the plugin. + * Only meant for use by advanced users. + * + * @method setPlayback + * @param {Object} value The new playback resource. + * @return {AbstractSoundInstance} Returns reference to itself for chaining calls + * @since 0.6.0 + **/ + p.setPlaybackResource = function (value) { + this._playbackResource = value; + if (this._duration == 0) { this._setDurationFromSource(); } + return this; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} can be accessed directly as a property, + * getPlaybackResource exists to allow support for IE8 with FlashAudioPlugin. + * + * An object containing any resources needed for audio playback, usually set by the plugin. + * + * @method setPlayback + * @param {Object} value The new playback resource. + * @return {Object} playback resource used for playing audio + * @since 0.6.0 + **/ + p.getPlaybackResource = function () { + return this._playbackResource; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} can be accessed directly as a property, + * getLoop exists to allow support for IE8 with FlashAudioPlugin. + * + * The number of play loops remaining. Negative values will loop infinitely. + * + * @method getLoop + * @return {number} + * @since 0.6.0 + **/ + p.getLoop = function () { + return this._loop; + }; + + /** + * NOTE {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} can be accessed directly as a property, + * setLoop exists to allow support for IE8 with FlashAudioPlugin. + * + * Set the number of play loops remaining. + * + * @method setLoop + * @param {number} value The number of times to loop after play. + * @since 0.6.0 + */ + p.setLoop = function (value) { + if(this._playbackResource != null) { + // remove looping + if (this._loop != 0 && value == 0) { + this._removeLooping(value); + } + // add looping + if (this._loop == 0 && value != 0) { + this._addLooping(value); + } + } + this._loop = value; + }; + + +// Private Methods: + /** + * A helper method that dispatches all events for AbstractSoundInstance. + * @method _sendEvent + * @param {String} type The event type + * @protected + */ + p._sendEvent = function (type) { + var event = new createjs.Event(type); + this.dispatchEvent(event); + }; + + /** + * Clean up the instance. Remove references and clean up any additional properties such as timers. + * @method _cleanUp + * @protected + */ + p._cleanUp = function () { + clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound + this._handleCleanUp(); + this._paused = false; + + createjs.Sound._playFinished(this); // TODO change to an event + }; + + /** + * The sound has been interrupted. + * @method _interrupt + * @protected + */ + p._interrupt = function () { + this._cleanUp(); + this.playState = createjs.Sound.PLAY_INTERRUPTED; + this._sendEvent("interrupted"); + }; + + /** + * Called by the Sound class when the audio is ready to play (delay has completed). Starts sound playing if the + * src is loaded, otherwise playback will fail. + * @method _beginPlaying + * @param {Number} offset How far into the sound to begin playback, in milliseconds. + * @param {Number} loop The number of times to loop the audio. Use -1 for infinite loops. + * @param {Number} volume The volume of the sound, between 0 and 1. + * @param {Number} pan The pan of the sound between -1 (left) and 1 (right). Note that pan does not work for HTML Audio. + * @return {Boolean} If playback succeeded. + * @protected + */ + p._beginPlaying = function (offset, loop, volume, pan) { + this.setPosition(offset); + this.setLoop(loop); + this.setVolume(volume); + this.setPan(pan); + + if (this._playbackResource != null && this._position < this._duration) { + this._paused = false; + this._handleSoundReady(); + this.playState = createjs.Sound.PLAY_SUCCEEDED; + this._sendEvent("succeeded"); + return true; + } else { + this._playFailed(); return false; - }; - this.getVolume = this.getPan = this.getDuration = function () { - return 0; } - this.playState = Sound.PLAY_FAILED; - this.toString = function () { - return "[Sound Default Sound Instance]"; - } - } + }; - Sound._defaultSoundInstance = new SoundInstance(); + /** + * Play has failed, which can happen for a variety of reasons. + * Cleans up instance and dispatches failed event + * @method _playFailed + * @private + */ + p._playFailed = function () { + this._cleanUp(); + this.playState = createjs.Sound.PLAY_FAILED; + this._sendEvent("failed"); + }; + + /** + * Audio has finished playing. Manually loop it if required. + * @method _handleSoundComplete + * @param event + * @protected + */ + p._handleSoundComplete = function (event) { + this._position = 0; // have to set this as it can be set by pause during playback + + if (this._loop != 0) { + this._loop--; // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1 + this._handleLoop(); + this._sendEvent("loop"); + return; + } + + this._cleanUp(); + this.playState = createjs.Sound.PLAY_FINISHED; + this._sendEvent("complete"); + }; + +// Plugin specific code + /** + * Handles starting playback when the sound is ready for playing. + * @method _handleSoundReady + * @protected + */ + p._handleSoundReady = function () { + // plugin specific code + }; + + /** + * Internal function used to update the volume based on the instance volume, master volume, instance mute value, + * and master mute value. + * @method _updateVolume + * @protected + */ + p._updateVolume = function () { + // plugin specific code + }; + + /** + * Internal function used to update the pan + * @method _updatePan + * @protected + * @since 0.6.0 + */ + p._updatePan = function () { + // plugin specific code + }; + + /** + * Internal function used to update the duration of the audio. + * @method _updateDuration + * @protected + * @since 0.6.0 + */ + p._updateDuration = function () { + // plugin specific code + }; + + /** + * Internal function used to get the duration of the audio from the source we'll be playing. + * @method _updateDuration + * @protected + * @since 0.6.0 + */ + p._setDurationFromSource = function () { + // plugin specific code + }; + + /** + * Internal function that calculates the current position of the playhead and sets it on this._position + * @method _updatePosition + * @protected + * @since 0.6.0 + */ + p._calculateCurrentPosition = function () { + // plugin specific code that sets this.position + }; + + /** + * Internal function used to update the position of the playhead. + * @method _updatePosition + * @protected + * @since 0.6.0 + */ + p._updatePosition = function () { + // plugin specific code + }; + + /** + * Internal function called when looping is removed during playback. + * @method _removeLooping + * @protected + * @since 0.6.0 + */ + p._removeLooping = function () { + // plugin specific code + }; + + /** + * Internal function called when looping is added during playback. + * @method _addLooping + * @protected + * @since 0.6.0 + */ + p._addLooping = function () { + // plugin specific code + }; + + /** + * Internal function called when pausing playback + * @method _pause + * @protected + * @since 0.6.0 + */ + p._pause = function () { + // plugin specific code + }; + + /** + * Internal function called when resuming playback + * @method _resume + * @protected + * @since 0.6.0 + */ + p._resume = function () { + // plugin specific code + }; + + /** + * Internal function called when stopping playback + * @method _handleStop + * @protected + * @since 0.6.0 + */ + p._handleStop = function() { + // plugin specific code + }; + + /** + * Internal function called when AbstractSoundInstance is being cleaned up + * @method _handleCleanUp + * @protected + * @since 0.6.0 + */ + p._handleCleanUp = function() { + // plugin specific code + }; + + /** + * Internal function called when AbstractSoundInstance has played to end and is looping + * @method _handleCleanUp + * @protected + * @since 0.6.0 + */ + p._handleLoop = function () { + // plugin specific code + }; + + createjs.AbstractSoundInstance = createjs.promote(AbstractSoundInstance, "EventDispatcher"); + createjs.DefaultSoundInstance = createjs.AbstractSoundInstance; // used when no plugin is supported +}()); + +//############################################################################## +// AbstractPlugin.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + +// constructor: + /** + * A default plugin class used as a base for all other plugins. + * @class AbstractPlugin + * @constructor + * @since 0.6.0 + */ + + var AbstractPlugin = function () { + // private properties: + /** + * The capabilities of the plugin. + * method and is used internally. + * @property _capabilities + * @type {Object} + * @default null + * @protected + * @static + */ + this._capabilities = null; + + /** + * Object hash indexed by the source URI of all created loaders, used to properly destroy them if sources are removed. + * @type {Object} + * @protected + */ + this._loaders = {}; + + /** + * Object hash indexed by the source URI of each file to indicate if an audio source has begun loading, + * is currently loading, or has completed loading. Can be used to store non boolean data after loading + * is complete (for example arrayBuffers for web audio). + * @property _audioSources + * @type {Object} + * @protected + */ + this._audioSources = {}; + + /** + * Object hash indexed by the source URI of all created SoundInstances, updates the playbackResource if it loads after they are created, + * and properly destroy them if sources are removed + * @type {Object} + * @protected + */ + this._soundInstances = {}; + + /** + * A reference to a loader class used by a plugin that must be set. + * @type {Object} + * @protected + */ + this._loaderClass; + + /** + * A reference to an AbstractSoundInstance class used by a plugin that must be set. + * @type {Object} + * @protected; + */ + this._soundInstanceClass; + }; + var p = AbstractPlugin.prototype; + + +// Static Properties: +// NOTE THESE PROPERTIES NEED TO BE ADDED TO EACH PLUGIN + /** + * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}} + * method and is used internally. + * @property _capabilities + * @type {Object} + * @default null + * @protected + * @static + */ + AbstractPlugin._capabilities = null; + + /** + * Determine if the plugin can be used in the current browser/OS. + * @method isSupported + * @return {Boolean} If the plugin can be initialized. + * @static + */ + AbstractPlugin.isSupported = function () { + return true; + }; + + +// public methods: + /** + * Pre-register a sound for preloading and setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}. + * Note all plugins provide a <code>Loader</code> instance, which <a href="http://preloadjs.com" target="_blank">PreloadJS</a> + * can use to assist with preloading. + * @method register + * @param {String} src The source of the audio + * @param {Number} instances The number of concurrently playing instances to allow for the channel at any time. + * Note that not every plugin will manage this value. + * @return {Object} A result object, containing a "tag" for preloading purposes. + */ + p.register = function (src, instances) { + this._audioSources[src] = true; + this._soundInstances[src] = []; + if(this._loaders[src]) {return this._loaders[src];} // already loading/loaded this, so don't load twice + // OJR potential issue that we won't be firing loaded event, might need to trigger if this is already loaded? + var loader = new this._loaderClass(src); + loader.on("complete", createjs.proxy(this._handlePreloadComplete, this)); + this._loaders[src] = loader; + return loader; + }; + + // note sound calls register before calling preload + /** + * Internally preload a sound. + * @method preload + * @param {Loader} loader The sound URI to load. + */ + p.preload = function (loader) { + loader.on("error", createjs.proxy(this._handlePreloadError, this)); + loader.load(); + }; + + /** + * Checks if preloading has started for a specific source. If the source is found, we can assume it is loading, + * or has already finished loading. + * @method isPreloadStarted + * @param {String} src The sound URI to check. + * @return {Boolean} + */ + p.isPreloadStarted = function (src) { + return (this._audioSources[src] != null); + }; + + /** + * Checks if preloading has finished for a specific source. + * @method isPreloadComplete + * @param {String} src The sound URI to load. + * @return {Boolean} + */ + p.isPreloadComplete = function (src) { + return (!(this._audioSources[src] == null || this._audioSources[src] == true)); + }; + + /** + * Remove a sound added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload. + * @method removeSound + * @param {String} src The sound URI to unload. + */ + p.removeSound = function (src) { + for (var i = this._soundInstances[src].length; i--; ) { + var item = this._soundInstances[src][i]; + item.destroy(); + } + delete(this._soundInstances[src]); + delete(this._audioSources[src]); + this._loaders[src].destroy(); + delete(this._loaders[src]); + }; + + /** + * Remove all sounds added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload. + * @method removeAllSounds + * @param {String} src The sound URI to unload. + */ + p.removeAllSounds = function () { + for(var key in this._audioSources) { + this.removeSound(key); + } + }; + + /** + * Create a sound instance. If the sound has not been preloaded, it is internally preloaded here. + * @method create + * @param {String} src The sound source to use. + * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds. + * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds. + * @return {AbstractSoundInstance} A sound instance for playback and control. + */ + p.create = function (src, startTime, duration) { + if (!this.isPreloadStarted(src)) { + this.preload(this.register(src)); + } + var si = new this._soundInstanceClass(src, startTime, duration, this._audioSources[src]); + this._soundInstances[src].push(si); + return si; + }; + + // TODO Volume & mute Getter / Setter?? + // TODO change calls to return nothing or this for chaining?? + // if a plugin does not support volume and mute, it should set these to null + /** + * Set the master volume of the plugin, which affects all SoundInstances. + * @method setVolume + * @param {Number} value The volume to set, between 0 and 1. + * @return {Boolean} If the plugin processes the setVolume call (true). The Sound class will affect all the + * instances manually otherwise. + */ + p.setVolume = function (value) { + this._volume = value; + this._updateVolume(); + return true; + }; + + /** + * Get the master volume of the plugin, which affects all SoundInstances. + * @method getVolume + * @return The volume level, between 0 and 1. + */ + p.getVolume = function () { + return this._volume; + }; + + /** + * Mute all sounds via the plugin. + * @method setMute + * @param {Boolean} value If all sound should be muted or not. Note that plugin-level muting just looks up + * the mute value of Sound {{#crossLink "Sound/getMute"}}{{/crossLink}}, so this property is not used here. + * @return {Boolean} If the mute call succeeds. + */ + p.setMute = function (value) { + this._updateVolume(); + return true; + }; + + // plugins should overwrite this method + p.toString = function () { + return "[AbstractPlugin]"; + }; + + +// private methods: + /** + * Handles internal preload completion. + * @method _handlePreloadComplete + * @protected + */ + p._handlePreloadComplete = function (event) { + var src = event.target.getItem().src; + this._audioSources[src] = event.result; + for (var i = 0, l = this._soundInstances[src].length; i < l; i++) { + var item = this._soundInstances[src][i]; + item.setPlaybackResource(this._audioSources[src]); + // ToDo consider adding play call here if playstate == playfailed + } + }; + + /** + * Handles internal preload erros + * @method _handlePreloadError + * @param event + * @protected + */ + p._handlePreloadError = function(event) { + //delete(this._audioSources[src]); + }; + + /** + * Set the gain value for master audio. Should not be called externally. + * @method _updateVolume + * @protected + */ + p._updateVolume = function () { + // Plugin Specific code + }; + + createjs.AbstractPlugin = AbstractPlugin; +}()); + +//############################################################################## +// WebAudioLoader.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + /** + * Loader provides a mechanism to preload Web Audio content via PreloadJS or internally. Instances are returned to + * the preloader, and the load method is called when the asset needs to be requested. + * + * @class WebAudioLoader + * @param {String} src The path to the sound + * @param {Object} flash The flash instance that will do the preloading. + * @extends XHRRequest + * @protected + */ + function Loader(src) { + this.AbstractLoader_constructor(src, true, createjs.AbstractLoader.SOUND); + + }; + var p = createjs.extend(Loader, createjs.AbstractLoader); + + /** + * web audio context required for decoding audio + * @property context + * @type {AudioContext} + * @static + */ + Loader.context = null; + + +// public methods + p.toString = function () { + return "[WebAudioLoader]"; + }; + + +// private methods + p._createRequest = function() { + this._request = new createjs.XHRRequest(this._item, false); + this._request.setResponseType("arraybuffer"); + }; + + p._sendComplete = function (event) { + // OJR we leave this wrapped in Loader because we need to reference src and the handler only receives a single argument, the decodedAudio + Loader.context.decodeAudioData(this._rawResult, + createjs.proxy(this._handleAudioDecoded, this), + createjs.proxy(this._handleError, this)); + }; /** - * An additional module to determine the current browser, version, operating system, and other environment - * variables. It is not publically documented. - * #class BrowserDetect - * @param {Boolean} isFirefox True if our browser is Firefox. - * @param {Boolean} isOpera True if our browser is opera. - * @param {Boolean} isChrome True if our browser is Chrome. Note that Chrome for Android returns true, but is a - * completely different browser with different abilities. - * @param {Boolean} isIOS True if our browser is safari for iOS devices (iPad, iPhone, and iPad). - * @param {Boolean} isAndroid True if our browser is Android. - * @param {Boolean} isBlackberry True if our browser is Blackberry. - * @constructor - * @static - */ - function BrowserDetect() { - } - - BrowserDetect.init = function () { - var agent = window.navigator.userAgent; - BrowserDetect.isWindowPhone = (agent.indexOf("IEMobile") > -1) || (agent.indexOf("Windows Phone") > -1); - BrowserDetect.isFirefox = (agent.indexOf("Firefox") > -1); - BrowserDetect.isOpera = (window.opera != null); - BrowserDetect.isChrome = (agent.indexOf("Chrome") > -1); // NOTE that Chrome on Android returns true but is a completely different browser with different abilities - BrowserDetect.isIOS = (agent.indexOf("iPod") > -1 || agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) && !BrowserDetect.isWindowPhone; - BrowserDetect.isAndroid = (agent.indexOf("Android") > -1); - BrowserDetect.isBlackberry = (agent.indexOf("Blackberry") > -1); + * The audio has been decoded. + * @method handleAudioDecoded + * @param decoded + * @protected + */ + p._handleAudioDecoded = function (decodedAudio) { + this._result = decodedAudio; + this.AbstractLoader__sendComplete(); }; - BrowserDetect.init(); - - createjs.Sound.BrowserDetect = BrowserDetect; - + createjs.WebAudioLoader = createjs.promote(Loader, "AbstractLoader"); }()); -/* - * WebAudioPlugin - * Visit http://createjs.com/ for documentation, updates and examples. - * - * - * Copyright (c) 2012 gskinner.com, inc. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ + +//############################################################################## +// WebAudioSoundInstance.js +//############################################################################## + +this.createjs = this.createjs || {}; /** - * @module SoundJS + * WebAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by + * {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. + * + * WebAudioSoundInstance exposes audioNodes for advanced users. + * + * @param {String} src The path to and file name of the sound. + * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds. + * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds. + * @param {Object} playbackResource Any resource needed by plugin to support audio playback. + * @class WebAudioSoundInstance + * @extends AbstractSoundInstance + * @constructor */ +(function () { + "use strict"; + + function WebAudioSoundInstance(src, startTime, duration, playbackResource) { + this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource); + + +// public properties + /** + * NOTE this is only intended for use by advanced users. + * <br />GainNode for controlling <code>WebAudioSoundInstance</code> volume. Connected to the {{#crossLink "WebAudioSoundInstance/destinationNode:property"}}{{/crossLink}}. + * @property gainNode + * @type {AudioGainNode} + * @since 0.4.0 + * + */ + this.gainNode = s.context.createGain(); + + /** + * NOTE this is only intended for use by advanced users. + * <br />A panNode allowing left and right audio channel panning only. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}. + * @property panNode + * @type {AudioPannerNode} + * @since 0.4.0 + */ + this.panNode = s.context.createPanner(); + this.panNode.panningModel = s._panningModel; + this.panNode.connect(this.gainNode); + + /** + * NOTE this is only intended for use by advanced users. + * <br />sourceNode is the audio source. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/panNode:property"}}{{/crossLink}}. + * @property sourceNode + * @type {AudioNode} + * @since 0.4.0 + * + */ + this.sourceNode = null; + + +// private properties + /** + * Timeout that is created internally to handle sound playing to completion. + * Stored so we can remove it when stop, pause, or cleanup are called + * @property _soundCompleteTimeout + * @type {timeoutVariable} + * @default null + * @protected + * @since 0.4.0 + */ + this._soundCompleteTimeout = null; + + /** + * NOTE this is only intended for use by very advanced users. + * _sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth + * looping. Connected to {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}. + * @property _sourceNodeNext + * @type {AudioNode} + * @default null + * @protected + * @since 0.4.1 + * + */ + this._sourceNodeNext = null; + + /** + * Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused. + * @property _playbackStartTime + * @type {Number} + * @default 0 + * @protected + * @since 0.4.0 + */ + this._playbackStartTime = 0; + + // Proxies, make removing listeners easier. + this._endedHandler = createjs.proxy(this._handleSoundComplete, this); + }; + var p = createjs.extend(WebAudioSoundInstance, createjs.AbstractSoundInstance); + var s = WebAudioSoundInstance; + + /** + * Note this is only intended for use by advanced users. + * <br />Audio context used to create nodes. This is and needs to be the same context used by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. + * @property context + * @type {AudioContext} + * @static + * @since 0.6.0 + */ + s.context = null; + + /** + * Note this is only intended for use by advanced users. + * <br /> Audio node from WebAudioPlugin that sequences to <code>context.destination</code> + * @property destinationNode + * @type {AudioNode} + * @static + * @since 0.6.0 + */ + s.destinationNode = null; + + /** + * Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation. + * @property _panningModel + * @type {Number / String} + * @protected + * @static + * @since 0.6.0 + */ + s._panningModel = "equalpower"; + + +// Public methods + p.destroy = function() { + this.AbstractSoundInstance_destroy(); + + this.panNode.disconnect(0); + this.panNode = null; + this.gainNode.disconnect(0); + this.gainNode = null; + }; + + p.toString = function () { + return "[WebAudioSoundInstance]"; + }; + + +// Private Methods + p._updatePan = function() { + this.panNode.setPosition(this._pan, 0, -0.5); + // z need to be -0.5 otherwise the sound only plays in left, right, or center + }; + + p._removeLooping = function() { + this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); + }; + + p._addLooping = function() { + if (this.playState != createjs.Sound.PLAY_SUCCEEDED) { return; } + this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0); + }; + + p._setDurationFromSource = function () { + this._duration = this.playbackResource.duration * 1000; + }; + + p._handleCleanUp = function () { + if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) { + this.sourceNode = this._cleanUpAudioNode(this.sourceNode); + this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); + } + + if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);} + // OJR there appears to be a bug that this doesn't always work in webkit (Chrome and Safari). According to the documentation, this should work. + + clearTimeout(this._soundCompleteTimeout); + + this._playbackStartTime = 0; // This is used by getPosition + }; + + /** + * Turn off and disconnect an audioNode, then set reference to null to release it for garbage collection + * @method _cleanUpAudioNode + * @param audioNode + * @return {audioNode} + * @protected + * @since 0.4.1 + */ + p._cleanUpAudioNode = function(audioNode) { + if(audioNode) { + audioNode.stop(0); + audioNode.disconnect(0); + audioNode = null; + } + return audioNode; + }; + + p._handleSoundReady = function (event) { + this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it. + + var dur = this._duration * 0.001; + var pos = this._position * 0.001; + this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos); + this._playbackStartTime = this.sourceNode.startTime - pos; + + this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos) * 1000); + + if(this._loop != 0) { + this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0); + } + }; + + /** + * Creates an audio node using the current src and context, connects it to the gain node, and starts playback. + * @method _createAndPlayAudioNode + * @param {Number} startTime The time to add this to the web audio context, in seconds. + * @param {Number} offset The amount of time into the src audio to start playback, in seconds. + * @return {audioNode} + * @protected + * @since 0.4.1 + */ + p._createAndPlayAudioNode = function(startTime, offset) { + var audioNode = s.context.createBufferSource(); + audioNode.buffer = this.playbackResource; + audioNode.connect(this.panNode); + var dur = this._duration * 0.001; + audioNode.startTime = startTime + dur; + audioNode.start(audioNode.startTime, offset+(this._startTime*0.001), dur - offset); + return audioNode; + }; + + p._pause = function () { + this._position = (s.context.currentTime - this._playbackStartTime) * 1000; // * 1000 to give milliseconds, lets us restart at same point + this.sourceNode = this._cleanUpAudioNode(this.sourceNode); + this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); + + if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);} + + clearTimeout(this._soundCompleteTimeout); + }; + + p._resume = function () { + this._handleSoundReady(); + }; + + /* + p._handleStop = function () { + // web audio does not need to do anything extra + }; + */ + + p._updateVolume = function () { + var newVolume = this._muted ? 0 : this._volume; + if (newVolume != this.gainNode.gain.value) { + this.gainNode.gain.value = newVolume; + } + }; + + p._calculateCurrentPosition = function () { + return ((s.context.currentTime - this._playbackStartTime) * 1000); // pos in seconds * 1000 to give milliseconds + }; + + p._updatePosition = function () { + this.sourceNode = this._cleanUpAudioNode(this.sourceNode); + this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); + clearTimeout(this._soundCompleteTimeout); + + if (!this._paused) {this._handleSoundReady();} + }; + + // OJR we are using a look ahead approach to ensure smooth looping. + // We add _sourceNodeNext to the audio context so that it starts playing even if this callback is delayed. + // This technique is described here: http://www.html5rocks.com/en/tutorials/audio/scheduling/ + // NOTE the cost of this is that our audio loop may not always match the loop event timing precisely. + p._handleLoop = function () { + this._cleanUpAudioNode(this.sourceNode); + this.sourceNode = this._sourceNodeNext; + this._playbackStartTime = this.sourceNode.startTime; + this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0); + this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration); + }; + + p._updateDuration = function () { + this._pause(); + this._resume(); + }; + + createjs.WebAudioSoundInstance = createjs.promote(WebAudioSoundInstance, "AbstractSoundInstance"); +}()); + +//############################################################################## +// WebAudioPlugin.js +//############################################################################## -// namespace: this.createjs = this.createjs || {}; (function () { @@ -2674,16 +6238,74 @@ this.createjs = this.createjs || {}; * by ensuring the audio and video audio share the same sampleRate.</li> * </ul> * @class WebAudioPlugin + * @extends AbstractPlugin * @constructor * @since 0.4.0 */ function WebAudioPlugin() { - this._init(); + this.AbstractPlugin_constructor(); + + +// Private Properties + /** + * Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation. + * @property _panningModel + * @type {Number / String} + * @protected + */ + this._panningModel = s._panningModel;; + + /** + * The internal master volume value of the plugin. + * @property _volume + * @type {Number} + * @default 1 + * @protected + */ + this._volume = 1; + + /** + * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin + * need to be created within this context. + * @property context + * @type {AudioContext} + */ + this.context = s.context; + + /** + * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion. + * It is connected to <code>context.destination</code>. + * + * Can be accessed by advanced users through createjs.Sound.activePlugin.dynamicsCompressorNode. + * @property dynamicsCompressorNode + * @type {AudioNode} + */ + this.dynamicsCompressorNode = this.context.createDynamicsCompressor(); + this.dynamicsCompressorNode.connect(this.context.destination); + + /** + * A GainNode for controlling master volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}. + * + * Can be accessed by advanced users through createjs.Sound.activePlugin.gainNode. + * @property gainNode + * @type {AudioGainNode} + */ + this.gainNode = this.context.createGain(); + this.gainNode.connect(this.dynamicsCompressorNode); + createjs.WebAudioSoundInstance.destinationNode = this.gainNode; + + this._capabilities = s._capabilities; + + this._loaderClass = createjs.WebAudioLoader; + this._soundInstanceClass = createjs.WebAudioSoundInstance; + + this._addPropsToClasses(); } + var p = createjs.extend(WebAudioPlugin, createjs.AbstractPlugin); +// Static Properties var s = WebAudioPlugin; - /** * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}} * method and is used internally. @@ -2695,6 +6317,30 @@ this.createjs = this.createjs || {}; */ s._capabilities = null; + /** + * Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation. + * @property _panningModel + * @type {Number / String} + * @protected + * @static + */ + s._panningModel = "equalpower"; + + /** + * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin + * need to be created within this context. + * + * Advanced users can set this to an existing context, but <b>must</b> do so before they call + * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}. + * + * @property context + * @type {AudioContext} + * @static + */ + s.context = null; + + +// Static Public Methods /** * Determine if the plugin can be used in the current browser/OS. * @method isSupported @@ -2703,7 +6349,7 @@ this.createjs = this.createjs || {}; */ s.isSupported = function () { // check if this is some kind of mobile device, Web Audio works with local protocol under PhoneGap and it is unlikely someone is trying to run a local file - var isMobilePhoneGap = createjs.Sound.BrowserDetect.isIOS || createjs.Sound.BrowserDetect.isAndroid || createjs.Sound.BrowserDetect.isBlackberry; + var isMobilePhoneGap = createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry; // OJR isMobile may be redundant with _isFileXHRSupported available. Consider removing. if (location.protocol == "file:" && !isMobilePhoneGap && !this._isFileXHRSupported()) { return false; } // Web Audio requires XHR, which is not usually available locally s._generateCapabilities(); @@ -2711,6 +6357,30 @@ this.createjs = this.createjs || {}; return true; }; + /** + * Plays an empty sound in the web audio context. This is used to enable web audio on iOS devices, as they + * require the first sound to be played inside of a user initiated event (touch/click). This is called when + * {{#crossLink "WebAudioPlugin"}}{{/crossLink}} is initialized (by Sound {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}} + * for example). + * + * <h4>Example</h4> + * function handleTouch(event) { + * createjs.WebAudioPlugin.playEmptySound(); + * } + * + * @method playEmptySound + * @static + * @since 0.4.1 + */ + s.playEmptySound = function() { + var source = s.context.createBufferSource(); + source.buffer = s.context.createBuffer(1, 1, 22050); + source.connect(s.context.destination); + source.start(0, 0, 0); + }; + + +// Static Private Methods /** * Determine if XHR is supported, which is necessary for web audio. * @method _isFileXHRSupported @@ -2757,12 +6427,14 @@ this.createjs = this.createjs || {}; var t = document.createElement("audio"); if (t.canPlayType == null) {return null;} - if (window.AudioContext) { - s.context = new AudioContext(); - } else if (window.webkitAudioContext) { - s.context = new webkitAudioContext(); - } else { - return null; + if (s.context == null) { + if (window.AudioContext) { + s.context = new AudioContext(); + } else if (window.webkitAudioContext) { + s.context = new webkitAudioContext(); + } else { + return null; + } } s._compatibilitySetUp(); @@ -2820,223 +6492,30 @@ this.createjs = this.createjs || {}; s._panningModel = 0; }; + +// Public Methods + p.toString = function () { + return "[WebAudioPlugin]"; + }; + + +// Private Methods /** - * Plays an empty sound in the web audio context. This is used to enable web audio on iOS devices, as they - * require the first sound to be played inside of a user initiated event (touch/click). This is called when - * {{#crossLink "WebAudioPlugin"}}{{/crossLink}} is initialized (by Sound {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}} - * for example). - * - * <h4>Example</h4> - * function handleTouch(event) { - * createjs.WebAudioPlugin.playEmptySound(); - * } - * - * @method playEmptySound + * Set up needed properties on supported classes WebAudioSoundInstance and WebAudioLoader. + * @method _addPropsToClasses * @static - * @since 0.4.1 - */ - s.playEmptySound = function() { - var source = s.context.createBufferSource(); - source.buffer = s.context.createBuffer(1, 1, 22050); - source.connect(s.context.destination); - source.start(0, 0, 0); - }; - - - var p = WebAudioPlugin.prototype; - p.constructor = WebAudioPlugin; - - p._capabilities = null; // doc'd above - - /** - * The internal master volume value of the plugin. - * @property _volume - * @type {Number} - * @default 1 * @protected + * @since 0.6.0 */ - p._volume = 1; + p._addPropsToClasses = function() { + var c = this._soundInstanceClass; + c.context = this.context; + c.destinationNode = this.gainNode; + c._panningModel = this._panningModel; - /** - * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin - * need to be created within this context. - * @property context - * @type {AudioContext} - */ - p.context = null; - - /** - * Value to set panning model to equal power for SoundInstance. Can be "equalpower" or 0 depending on browser implementation. - * @property _panningModel - * @type {Number / String} - * @protected - */ - p._panningModel = "equalpower"; - - /** - * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion. - * It is connected to <code>context.destination</code>. - * - * Can be accessed by advanced users through createjs.Sound.activePlugin.dynamicsCompressorNode. - * @property dynamicsCompressorNode - * @type {AudioNode} - */ - p.dynamicsCompressorNode = null; - - /** - * A GainNode for controlling master volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}. - * - * Can be accessed by advanced users through createjs.Sound.activePlugin.gainNode. - * @property gainNode - * @type {AudioGainNode} - */ - p.gainNode = null; - - /** - * An object hash used internally to store ArrayBuffers, indexed by the source URI used to load it. This - * prevents having to load and decode audio files more than once. If a load has been started on a file, - * <code>arrayBuffers[src]</code> will be set to true. Once load is complete, it is set the the loaded - * ArrayBuffer instance. - * @property _arrayBuffers - * @type {Object} - * @protected - */ - p._arrayBuffers = null; - - /** - * An initialization function run by the constructor - * @method _init - * @protected - */ - p._init = function () { - this._capabilities = s._capabilities; - this._arrayBuffers = {}; - - this.context = s.context; - this._panningModel = s._panningModel; - - // set up AudioNodes that all of our source audio will connect to - this.dynamicsCompressorNode = this.context.createDynamicsCompressor(); - this.dynamicsCompressorNode.connect(this.context.destination); - this.gainNode = this.context.createGain(); - this.gainNode.connect(this.dynamicsCompressorNode); + this._loaderClass.context = this.context; }; - /** - * Pre-register a sound for preloading and setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}. - * Note that WebAudio provides a <code>Loader</code> instance, which <a href="http://preloadjs.com" target="_blank">PreloadJS</a> - * can use to assist with preloading. - * @method register - * @param {String} src The source of the audio - * @param {Number} instances The number of concurrently playing instances to allow for the channel at any time. - * Note that the WebAudioPlugin does not manage this property. - * @return {Object} A result object, containing a "tag" for preloading purposes. - */ - p.register = function (src, instances) { - this._arrayBuffers[src] = true; - var loader = {tag: new createjs.WebAudioPlugin.Loader(src, this)}; - return loader; - }; - - /** - * Checks if preloading has started for a specific source. If the source is found, we can assume it is loading, - * or has already finished loading. - * @method isPreloadStarted - * @param {String} src The sound URI to check. - * @return {Boolean} - */ - p.isPreloadStarted = function (src) { - return (this._arrayBuffers[src] != null); - }; - - /** - * Checks if preloading has finished for a specific source. - * @method isPreloadComplete - * @param {String} src The sound URI to load. - * @return {Boolean} - */ - p.isPreloadComplete = function (src) { - return (!(this._arrayBuffers[src] == null || this._arrayBuffers[src] == true)); - }; - - /** - * Remove a sound added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload. - * @method removeSound - * @param {String} src The sound URI to unload. - * @since 0.4.1 - */ - p.removeSound = function (src) { - delete(this._arrayBuffers[src]); - }; - - /** - * Remove all sounds added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload. - * @method removeAllSounds - * @param {String} src The sound URI to unload. - * @since 0.4.1 - */ - p.removeAllSounds = function () { - this._arrayBuffers = {}; - }; - - /** - * Add loaded results to the preload object hash. - * @method addPreloadResults - * @param {String} src The sound URI to unload. - * @return {Boolean} - */ - p.addPreloadResults = function (src, result) { - this._arrayBuffers[src] = result; - }; - - /** - * Handles internal preload completion. - * @method _handlePreloadComplete - * @protected - */ - p._handlePreloadComplete = function (loader) { - createjs.Sound._sendFileLoadEvent(loader.src); - loader.cleanUp(); - }; - - /** - * Internally preload a sound. Loading uses XHR2 to load an array buffer for use with WebAudio. - * @method preload - * @param {String} src The sound URI to load. - * @param {Object} tag Not used in this plugin. - */ - p.preload = function (src, tag) { - this._arrayBuffers[src] = true; - var loader = new createjs.WebAudioPlugin.Loader(src, this); - loader.onload = this._handlePreloadComplete; - loader.load(); - }; - - /** - * Create a sound instance. If the sound has not been preloaded, it is internally preloaded here. - * @method create - * @param {String} src The sound source to use. - * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds. - * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds. - * @return {SoundInstance} A sound instance for playback and control. - */ - p.create = function (src, startTime, duration) { - if (!this.isPreloadStarted(src)) {this.preload(src);} - return new createjs.WebAudioPlugin.SoundInstance(src, startTime, duration, this); - }; - - /** - * Set the master volume of the plugin, which affects all SoundInstances. - * @method setVolume - * @param {Number} value The volume to set, between 0 and 1. - * @return {Boolean} If the plugin processes the setVolume call (true). The Sound class will affect all the - * instances manually otherwise. - */ - p.setVolume = function (value) { - this._volume = value; - this._updateVolume(); - return true; - }; /** * Set the gain value for master audio. Should not be called externally. @@ -3050,1922 +6529,83 @@ this.createjs = this.createjs || {}; } }; - /** - * Get the master volume of the plugin, which affects all SoundInstances. - * @method getVolume - * @return The volume level, between 0 and 1. - */ - p.getVolume = function () { - return this._volume; - }; - - /** - * Mute all sounds via the plugin. - * @method setMute - * @param {Boolean} value If all sound should be muted or not. Note that plugin-level muting just looks up - * the mute value of Sound {{#crossLink "Sound/getMute"}}{{/crossLink}}, so this property is not used here. - * @return {Boolean} If the mute call succeeds. - */ - p.setMute = function (value) { - this._updateVolume(); - return true; - }; - - p.toString = function () { - return "[WebAudioPlugin]"; - }; - - createjs.WebAudioPlugin = WebAudioPlugin; + createjs.WebAudioPlugin = createjs.promote(WebAudioPlugin, "AbstractPlugin"); }()); -(function () { +//############################################################################## +// HTMLAudioTagPool.js +//############################################################################## - "use strict"; - - /** - * A SoundInstance is created when any calls to the Sound API method {{#crossLink "Sound/play"}}{{/crossLink}} or - * {{#crossLink "Sound/createInstance"}}{{/crossLink}} are made. The SoundInstance is returned by the active plugin - * for control by the user. - * - * <h4>Example</h4> - * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3"); - * - * A number of additional parameters provide a quick way to determine how a sound is played. Please see the Sound - * API method {{#crossLink "Sound/play"}}{{/crossLink}} for a list of arguments. - * - * Once a SoundInstance is created, a reference can be stored that can be used to control the audio directly through - * the SoundInstance. If the reference is not stored, the SoundInstance will play out its audio (and any loops), and - * is then de-referenced from the {{#crossLink "Sound"}}{{/crossLink}} class so that it can be cleaned up. If audio - * playback has completed, a simple call to the {{#crossLink "SoundInstance/play"}}{{/crossLink}} instance method - * will rebuild the references the Sound class need to control it. - * - * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3", {loop:2}); - * myInstance.addEventListener("loop", handleLoop); - * function handleLoop(event) { - * myInstance.volume = myInstance.volume * 0.5; - * } - * - * Events are dispatched from the instance to notify when the sound has completed, looped, or when playback fails - * - * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3"); - * myInstance.addEventListener("complete", handleComplete); - * myInstance.addEventListener("loop", handleLoop); - * myInstance.addEventListener("failed", handleFailed); - * - * - * @class SoundInstance - * @param {String} src The path to and file name of the sound. - * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds. - * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds. - * @param {Object} owner The plugin instance that created this SoundInstance. - * @extends EventDispatcher - * @constructor - */ - function SoundInstance(src, startTime, duration, owner) { - this._init(src, startTime, duration, owner); - } - - var p = SoundInstance.prototype = new createjs.EventDispatcher(); - p.constructor = SoundInstance; - - /** - * The source of the sound. - * @property src - * @type {String} - * @default null - */ - p.src = null; - - /** - * The unique ID of the instance. This is set by {{#crossLink "Sound"}}{{/crossLink}}. - * @property uniqueId - * @type {String} | Number - * @default -1 - */ - p.uniqueId = -1; - - /** - * The play state of the sound. Play states are defined as constants on {{#crossLink "Sound"}}{{/crossLink}}. - * @property playState - * @type {String} - * @default null - */ - p.playState = null; - - /** - * The plugin that created the instance - * @property _owner - * @type {WebAudioPlugin} - * @default null - * @protected - */ - p._owner = null; - - /** - * How far into the sound to begin playback in milliseconds. This is passed in when play is called and used by - * pause and setPosition to track where the sound should be at. - * Note this is converted from milliseconds to seconds for consistency with the WebAudio API. - * @property _offset - * @type {Number} - * @default 0 - * @protected - */ - p._offset = 0; - - /** - * Audio sprite property used to determine the starting offset. - * @type {Number} - * @default null - * @protected - */ - p._startTime = 0; - - /** - * The volume of the sound, between 0 and 1. - * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower and Opera versions 11.50 or lower, - * and Internet Explorer 8 or lower. Instead use {{#crossLink "SoundInstance/setVolume"}}{{/crossLink}} and {{#crossLink "SoundInstance/getVolume"}}{{/crossLink}}. - * - * The actual output volume of a sound can be calculated using: - * <code>myInstance.volume * createjs.Sound.getVolume();</code> - * - * @property volume - * @type {Number} - * @default 1 - */ - p._volume = 1; - if (createjs.definePropertySupported) { - Object.defineProperty(p, "volume", { - get: function() { - return this._volume; - }, - set: function(value) { - if (Number(value) == null) {return false} - value = Math.max(0, Math.min(1, value)); - this._volume = value; - this._updateVolume(); - } - }); - } - - /** - * The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio. - * - * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower, - * and Internet Explorer 8 or lower. Instead use {{#crossLink "SoundInstance/setPan"}}{{/crossLink}} and {{#crossLink "SoundInstance/getPan"}}{{/crossLink}}. - * <br />Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio. - * - * @property pan - * @type {Number} - * @default 0 - */ - p._pan = 0; - if (createjs.definePropertySupported) { - Object.defineProperty(p, "pan", { - get: function() { - return this._pan; - }, - set: function(value) { - if (!this._owner._capabilities.panning || Number(value) == null) {return false;} - - value = Math.max(-1, Math.min(1, value)); // force pan to stay in the -1 to 1 range - // Note that panning in WebAudioPlugin can support 3D audio, but our implementation does not. - this._pan = value; // Unfortunately panner does not give us a way to access this after it is set http://www.w3.org/TR/webaudio/#AudioPannerNode - this.panNode.setPosition(value, 0, -0.5); // z need to be -0.5 otherwise the sound only plays in left, right, or center - } - }); - } - -/** - * The length of the audio clip, in milliseconds. - * Use {{#crossLink "SoundInstance/getDuration:method"}}{{/crossLink}} to access. - * @property _duration - * @type {Number} - * @default 0 - * @protected - */ - p._duration = 0; - - /** - * The number of play loops remaining. Negative values will loop infinitely. - * - * @property loop - * @type {Number} - * @default 0 - * @public - */ - p._remainingLoops = 0; - if (createjs.definePropertySupported) { - Object.defineProperty(p, "loop", { - get: function() { - return this._remainingLoops; - }, - set: function(value) { - // remove looping - if (this._remainingLoops != 0 && value == 0) { - this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); - } - // add looping - if (this._remainingLoops == 0 && value != 0) { - this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0); - } - this._remainingLoops = value; - } - }); - } - - /** - * A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this SoundInstance is played with a delay. - * This allows SoundInstance to remove the delay if stop, pause, or cleanup are called before playback begins. - * @property _delayTimeoutId - * @type {timeoutVariable} - * @default null - * @protected - * @since 0.4.0 - */ - p._delayTimeoutId = null; - - /** - * Timeout that is created internally to handle sound playing to completion. Stored so we can remove it when - * stop, pause, or cleanup are called - * @property _soundCompleteTimeout - * @type {timeoutVariable} - * @default null - * @protected - * @since 0.4.0 - */ - p._soundCompleteTimeout = null; - - /** - * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users. - * <br />GainNode for controlling <code>SoundInstance</code> volume. Connected to the WebAudioPlugin {{#crossLink "WebAudioPlugin/gainNode:property"}}{{/crossLink}} - * that sequences to <code>context.destination</code>. - * @property gainNode - * @type {AudioGainNode} - * @since 0.4.0 - * - */ - p.gainNode = null; - - /** - * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users. - * <br />A panNode allowing left and right audio channel panning only. Connected to SoundInstance {{#crossLink "SoundInstance/gainNode:property"}}{{/crossLink}}. - * @property panNode - * @type {AudioPannerNode} - * @since 0.4.0 - */ - p.panNode = null; - - /** - * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users. - * <br />sourceNode is the audio source. Connected to SoundInstance {{#crossLink "SoundInstance/panNode:property"}}{{/crossLink}}. - * @property sourceNode - * @type {AudioNode} - * @since 0.4.0 - * - */ - p.sourceNode = null; - - /** - * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users. - * _sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth - * looping. Connected to {{#crossLink "SoundInstance/gainNode:property"}}{{/crossLink}}. - * @property _sourceNodeNext - * @type {AudioNode} - * @default null - * @protected - * @since 0.4.1 - * - */ - p._sourceNodeNext = null; - - /** - * Determines if the audio is currently muted. - * Use {{#crossLink "SoundInstance/getMute:method"}}{{/crossLink}} and {{#crossLink "SoundInstance/setMute:method"}}{{/crossLink}} to access. - * @property _muted - * @type {Boolean} - * @default false - * @protected - */ - p._muted = false; - - /** - * Read only value that tells you if the audio is currently paused. - * Use {{#crossLink "SoundInstance/pause:method"}}{{/crossLink}} and {{#crossLink "SoundInstance/resume:method"}}{{/crossLink}} to set. - * @property paused - * @type {Boolean} - */ - p.paused = false; // this value will not be used, and is only set - p._paused = false; // this value is used internally for setting paused - - /** - * WebAudioPlugin only. - * Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused. - * @property _playbackStartTime - * @type {Number} - * @default 0 - * @protected - * @since 0.4.0 - */ - p._playbackStartTime = 0; - - // Proxies, make removing listeners easier. - p._endedHandler = null; - -// Events - /** - * The event that is fired when playback has started successfully. - * @event succeeded - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @since 0.4.0 - */ - - /** - * The event that is fired when playback is interrupted. This happens when another sound with the same - * src property is played using an interrupt value that causes this instance to stop playing. - * @event interrupted - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @since 0.4.0 - */ - - /** - * The event that is fired when playback has failed. This happens when there are too many channels with the same - * src property already playing (and the interrupt value doesn't cause an interrupt of another instance), or - * the sound could not be played, perhaps due to a 404 error. - * @event failed - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @since 0.4.0 - */ - - /** - * The event that is fired when a sound has completed playing but has loops remaining. - * @event loop - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @since 0.4.0 - */ - - /** - * The event that is fired when playback completes. This means that the sound has finished playing in its - * entirety, including its loop iterations. - * @event complete - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @since 0.4.0 - */ - - /** - * A helper method that dispatches all events for SoundInstance. - * @method _sendEvent - * @param {String} type The event type - * @protected - */ - p._sendEvent = function (type) { - var event = new createjs.Event(type); - this.dispatchEvent(event); - }; - -// Constructor - /** - * Initialize the SoundInstance. This is called from the constructor. - * @method _init - * @param {string} src The source of the audio. - * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds. - * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds. - * @param {Class} owner The plugin that created this instance. - * @protected - */ - p._init = function (src, startTime, duration, owner) { - this.src = src; - this._startTime = startTime * 0.001 || 0; // convert ms to s as web audio handles everything in seconds - this._duration = duration || 0; - this._owner = owner; - - this.gainNode = this._owner.context.createGain(); - - this.panNode = this._owner.context.createPanner(); - this.panNode.panningModel = this._owner._panningModel; - this.panNode.connect(this.gainNode); - - if (this._owner.isPreloadComplete(this.src) && !this._duration) {this._duration = this._owner._arrayBuffers[this.src].duration * 1000;} - - this._endedHandler = createjs.proxy(this._handleSoundComplete, this); - }; - - /** - * Clean up the instance. Remove references and clean up any additional properties such as timers. - * @method _cleanUp - * @protected - */ - p._cleanUp = function () { - if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) { - this.sourceNode = this._cleanUpAudioNode(this.sourceNode); - this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); - } - - if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);} - // OJR there appears to be a bug that this doesn't always work in webkit (Chrome and Safari). According to the documentation, this should work. - - clearTimeout(this._delayTimeoutId); // clear timeout that plays delayed sound - clearTimeout(this._soundCompleteTimeout); // clear timeout that triggers sound complete - - this._playbackStartTime = 0; // This is used by getPosition - - createjs.Sound._playFinished(this); - }; - - /** - * Turn off and disconnect an audioNode, then set reference to null to release it for garbage collection - * @method _cleanUpAudioNode - * @param audioNode - * @return {audioNode} - * @protected - * @since 0.4.1 - */ - p._cleanUpAudioNode = function(audioNode) { - if(audioNode) { - audioNode.stop(0); - audioNode.disconnect(0); - audioNode = null; - } - return audioNode; - }; - - /** - * The sound has been interrupted. - * @method _interrupt - * @protected - */ - p._interrupt = function () { - this._cleanUp(); - this.playState = createjs.Sound.PLAY_INTERRUPTED; - this.paused = this._paused = false; - this._sendEvent("interrupted"); - }; - - /** - * Handles starting playback when the sound is ready for playing. - * @method _handleSoundReady - * @protected - */ - p._handleSoundReady = function (event) { - if (!this._duration) {this._duration = this._owner._arrayBuffers[this.src].duration * 1000;} // NOTE *1000 because WebAudio reports everything in seconds but js uses milliseconds - if ((this._offset*1000) > this._duration) { - this.playFailed(); - return; - } else if (this._offset < 0) { // may not need this check if play ignores negative values, this is not specified in the API http://www.w3.org/TR/webaudio/#AudioBufferSourceNode - this._offset = 0; - } - - this.playState = createjs.Sound.PLAY_SUCCEEDED; - this.paused = this._paused = false; - - this.gainNode.connect(this._owner.gainNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it. - - var dur = this._duration * 0.001; - this.sourceNode = this._createAndPlayAudioNode((this._owner.context.currentTime - dur), this._offset); - this._playbackStartTime = this.sourceNode.startTime - this._offset; - - this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - this._offset) * 1000); - - if(this._remainingLoops != 0) { - this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0); - } - }; - - /** - * Creates an audio node using the current src and context, connects it to the gain node, and starts playback. - * @method _createAndPlayAudioNode - * @param {Number} startTime The time to add this to the web audio context, in seconds. - * @param {Number} offset The amount of time into the src audio to start playback, in seconds. - * @return {audioNode} - * @protected - * @since 0.4.1 - */ - p._createAndPlayAudioNode = function(startTime, offset) { - var audioNode = this._owner.context.createBufferSource(); - audioNode.buffer = this._owner._arrayBuffers[this.src]; - audioNode.connect(this.panNode); - var dur = this._duration * 0.001; - audioNode.startTime = startTime + dur; - audioNode.start(audioNode.startTime, offset+this._startTime, dur - offset); - return audioNode; - }; - - // Public API - /** - * Play an instance. This method is intended to be called on SoundInstances that already exist (created - * with the Sound API {{#crossLink "Sound/createInstance"}}{{/crossLink}} or {{#crossLink "Sound/play"}}{{/crossLink}}). - * - * <h4>Example</h4> - * var myInstance = createjs.Sound.createInstance(mySrc); - * myInstance.play({offset:1, loop:2, pan:0.5}); // options as object properties - * myInstance.play(createjs.Sound.INTERRUPT_ANY); // options as parameters - * - * Note that if this sound is already playing, this call will do nothing. - * - * @method play - * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source, - * if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code> - * constants on the Sound class, with the default defined by Sound {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}. - * <br /><strong>OR</strong><br /> - * This parameter can be an object that contains any or all optional properties by name, including: interrupt, - * delay, offset, loop, volume, and pan (see the above code sample). - * @param {Number} [delay=0] The delay in milliseconds before the sound starts - * @param {Number} [offset=0] How far into the sound to begin playback, in milliseconds. - * @param {Number} [loop=0] The number of times to loop the audio. Use -1 for infinite loops. - * @param {Number} [volume=1] The volume of the sound, between 0 and 1. - * @param {Number} [pan=0] The pan of the sound between -1 (left) and 1 (right). Note that pan is not supported - * for HTML Audio. - */ - p.play = function (interrupt, delay, offset, loop, volume, pan) { - if (this.playState == createjs.Sound.PLAY_SUCCEEDED) { - if (interrupt instanceof Object) { - offset = interrupt.offset; - loop = interrupt.loop; - volume = interrupt.volume; - pan = interrupt.pan; - } - if (offset != null) { this.setPosition(offset) } - if (loop != null) { this.loop = loop; } - if (volume != null) { this.setVolume(volume); } - if (pan != null) { this.setPan(pan); } - if (this._paused) { this.resume(); } - return; - } - this._cleanUp(); - createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan); - }; - - /** - * Called by the Sound class when the audio is ready to play (delay has completed). Starts sound playing if the - * src is loaded, otherwise playback will fail. - * @method _beginPlaying - * @param {Number} offset How far into the sound to begin playback, in milliseconds. - * @param {Number} loop The number of times to loop the audio. Use -1 for infinite loops. - * @param {Number} volume The volume of the sound, between 0 and 1. - * @param {Number} pan The pan of the sound between -1 (left) and 1 (right). Note that pan does not work for HTML Audio. - * @protected - */ - p._beginPlaying = function (offset, loop, volume, pan) { - this._offset = offset * 0.001; //convert ms to sec - this._remainingLoops = loop; - this.volume = volume; - this.pan = pan; - - if (this._owner.isPreloadComplete(this.src)) { - this._handleSoundReady(null); - this._sendEvent("succeeded"); - return 1; - } else { - this.playFailed(); - return; - } - }; - - /** - * Pause the instance. Paused audio will stop at the current time, and can be resumed using - * {{#crossLink "SoundInstance/resume"}}{{/crossLink}}. - * - * <h4>Example</h4> - * - * myInstance.pause(); - * - * @method pause - * @return {Boolean} If the pause call succeeds. This will return false if the sound isn't currently playing. - */ - p.pause = function () { - if (this._paused || this.playState != createjs.Sound.PLAY_SUCCEEDED) {return false;} - - this.paused = this._paused = true; - - this._offset = this._owner.context.currentTime - this._playbackStartTime; // this allows us to restart the sound at the same point in playback - this.sourceNode = this._cleanUpAudioNode(this.sourceNode); - this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); - - if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);} - - clearTimeout(this._delayTimeoutId); - clearTimeout(this._soundCompleteTimeout); - return true; - }; - - /** - * Resume an instance that has been paused using {{#crossLink "SoundInstance/pause"}}{{/crossLink}}. Audio that - * has not been paused will not playback when this method is called. - * - * <h4>Example</h4> - * - * myInstance.pause(); - * // do some stuff - * myInstance.resume(); - * - * @method resume - * @return {Boolean} If the resume call succeeds. This will return false if called on a sound that is not paused. - */ - p.resume = function () { - if (!this._paused) {return false;} - this._handleSoundReady(); - return true; - }; - - /** - * Stop playback of the instance. Stopped sounds will reset their position to 0, and calls to {{#crossLink "SoundInstance/resume"}}{{/crossLink}} - * will fail. To start playback again, call {{#crossLink "SoundInstance/play"}}{{/crossLink}}. - * - * <h4>Example</h4> - * - * myInstance.stop(); - * - * @method stop - * @return {Boolean} If the stop call succeeds. - */ - p.stop = function () { - this.paused = this._paused = false; - this._cleanUp(); - this.playState = createjs.Sound.PLAY_FINISHED; - this._offset = 0; // set audio to start at the beginning - return true; - }; - - /** - * NOTE that you can set volume directly as a property, and setVolume remains to allow support for IE8 with FlashPlugin. - * Set the volume of the instance. You can retrieve the volume using {{#crossLink "SoundInstance/getVolume"}}{{/crossLink}}. - * - * <h4>Example</h4> - * - * myInstance.setVolume(0.5); - * - * Note that the master volume set using the Sound API method {{#crossLink "Sound/setVolume"}}{{/crossLink}} - * will be applied to the instance volume. - * - * @method setVolume - * @param value The volume to set, between 0 and 1. - * @return {Boolean} If the setVolume call succeeds. - */ - p.setVolume = function (value) { - this.volume = value; - return true; - }; - - /** - * Internal function used to update the volume based on the instance volume, master volume, instance mute value, - * and master mute value. - * @method _updateVolume - * @protected - */ - p._updateVolume = function () { - var newVolume = this._muted ? 0 : this._volume; - if (newVolume != this.gainNode.gain.value) { - this.gainNode.gain.value = newVolume; - } - }; - - /** - * NOTE that you can access volume directly as a property, and getVolume remains to allow support for IE8 with FlashPlugin. - * - * Get the volume of the instance. The actual output volume of a sound can be calculated using: - * <code>myInstance.getVolume() * createjs.Sound.getVolume();</code> - * - * @method getVolume - * @return The current volume of the sound instance. - */ - p.getVolume = function () { - return this.volume; - }; - - /** - * Mute and unmute the sound. Muted sounds will still play at 0 volume. Note that an unmuted sound may still be - * silent depending on {{#crossLink "Sound"}}{{/crossLink}} volume, instance volume, and Sound mute. - * - * <h4>Example</h4> - * - * myInstance.setMute(true); - * - * @method setMute - * @param {Boolean} value If the sound should be muted. - * @return {Boolean} If the mute call succeeds. - * @since 0.4.0 - */ - p.setMute = function (value) { - if (value == null) {return false;} - - this._muted = value; - this._updateVolume(); - return true; - }; - - /** - * Get the mute value of the instance. - * - * <h4>Example</h4> - * - * var isMuted = myInstance.getMute(); - * - * @method getMute - * @return {Boolean} If the sound is muted. - * @since 0.4.0 - */ - p.getMute = function () { - return this._muted; - }; - - /** - * NOTE that you can set pan directly as a property, and getPan remains to allow support for IE8 with FlashPlugin. - * - * Set the left(-1)/right(+1) pan of the instance. Note that {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}} does not - * support panning, and only simple left/right panning has been implemented for {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. - * The default pan value is 0 (center). - * - * <h4>Example</h4> - * - * myInstance.setPan(-1); // to the left! - * - * @method setPan - * @param {Number} value The pan value, between -1 (left) and 1 (right). - * @return {Number} If the setPan call succeeds. - */ - p.setPan = function (value) { - this.pan = value; // Unfortunately panner does not give us a way to access this after it is set http://www.w3.org/TR/webaudio/#AudioPannerNode - if(this.pan != value) {return false;} - return true; - }; - - /** - * NOTE that you can access pan directly as a property, and getPan remains to allow support for IE8 with FlashPlugin. - * - * Get the left/right pan of the instance. Note in WebAudioPlugin this only gives us the "x" value of what is - * actually 3D audio. - * - * <h4>Example</h4> - * - * var myPan = myInstance.getPan(); - * - * @method getPan - * @return {Number} The value of the pan, between -1 (left) and 1 (right). - */ - p.getPan = function () { - return this.pan; - }; - - /** - * Get the position of the playhead of the instance in milliseconds. - * - * <h4>Example</h4> - * - * var currentOffset = myInstance.getPosition(); - * - * @method getPosition - * @return {Number} The position of the playhead in the sound, in milliseconds. - */ - p.getPosition = function () { - if (this._paused || this.sourceNode == null) { - var pos = this._offset; - } else { - var pos = this._owner.context.currentTime - this._playbackStartTime; - } - - return pos * 1000; // pos in seconds * 1000 to give milliseconds - }; - - /** - * Set the position of the playhead in the instance. This can be set while a sound is playing, paused, or - * stopped. - * - * <h4>Example</h4> - * - * myInstance.setPosition(myInstance.getDuration()/2); // set audio to its halfway point. - * - * @method setPosition - * @param {Number} value The position to place the playhead, in milliseconds. - */ - p.setPosition = function (value) { - this._offset = value * 0.001; // convert milliseconds to seconds - - if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) { - // we need to stop this sound from continuing to play, as we need to recreate the sourceNode to change position - this.sourceNode = this._cleanUpAudioNode(this.sourceNode); - this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); - clearTimeout(this._soundCompleteTimeout); // clear timeout that triggers sound complete - } // NOTE we cannot just call cleanup because it also calls the Sound function _playFinished which releases this instance in SoundChannel - - if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {this._handleSoundReady();} - - return true; - }; - - /** - * Get the duration of the instance, in milliseconds. Note in most cases, you need to play a sound using - * {{#crossLink "SoundInstance/play"}}{{/crossLink}} or the Sound API {{#crossLink "Sound/play"}}{{/crossLink}} - * method before its duration can be reported accurately. - * - * <h4>Example</h4> - * - * var soundDur = myInstance.getDuration(); - * - * @method getDuration - * @return {Number} The duration of the sound instance in milliseconds. - */ - p.getDuration = function () { - return this._duration; - }; - - /** - * Audio has finished playing. Manually loop it if required. - * @method _handleSoundComplete - * @param event - * @protected - */ - // called internally by _soundCompleteTimeout in WebAudioPlugin - p._handleSoundComplete = function (event) { - this._offset = 0; // have to set this as it can be set by pause during playback - - if (this._remainingLoops != 0) { - this._remainingLoops--; // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1 - - // OJR we are using a look ahead approach to ensure smooth looping. We add _sourceNodeNext to the audio - // context so that it starts playing even if this callback is delayed. This technique and the reasons for - // using it are described in greater detail here: http://www.html5rocks.com/en/tutorials/audio/scheduling/ - // NOTE the cost of this is that our audio loop may not always match the loop event timing precisely. - if(this._sourceNodeNext) { // this can be set to null, but this should not happen when looping - this._cleanUpAudioNode(this.sourceNode); - this.sourceNode = this._sourceNodeNext; - this._playbackStartTime = this.sourceNode.startTime; - this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0); - this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration); - } - else { - this._handleSoundReady(); - } - - this._sendEvent("loop"); - return; - } - - this._cleanUp(); - this.playState = createjs.Sound.PLAY_FINISHED; - this._sendEvent("complete"); - }; - - // Play has failed, which can happen for a variety of reasons. - p.playFailed = function () { - this._cleanUp(); - this.playState = createjs.Sound.PLAY_FAILED; - this._sendEvent("failed"); - }; - - p.toString = function () { - return "[WebAudioPlugin SoundInstance]"; - }; - - createjs.WebAudioPlugin.SoundInstance = SoundInstance; -}()); - -(function () { - - "use strict"; - - /** - * An internal helper class that preloads web audio via XHR. Note that this class and its methods are not documented - * properly to avoid generating HTML documentation. - * #class Loader - * @param {String} src The source of the sound to load. - * @param {Object} owner A reference to the class that created this instance. - * @constructor - */ - function Loader(src, owner) { - this._init(src, owner); - } - - var p = Loader.prototype; - p.constructor = Loader; - - // the request object for or XHR2 request - p.request = null; - - p.owner = null; - p.progress = -1; - - /** - * The source of the sound to load. Used by callback functions when we return this class. - * #property src - * @type {String} - */ - p.src = null; - - /** - * The decoded AudioBuffer array that is returned when loading is complete. - * #property result - * @type {AudioBuffer} - * @protected - */ - p.result = null; - - // Calbacks - /** - * The callback that fires when the load completes. This follows HTML tag naming. - * #property onload - * @type {Method} - */ - p.onload = null; - - /** - * The callback that fires as the load progresses. This follows HTML tag naming. - * #property onprogress - * @type {Method} - */ - p.onprogress = null; - - /** - * The callback that fires if the load hits an error. This follows HTML tag naming. - * #property onerror - * @type {Method} - * @protected - */ - p.onerror = null; - - // constructor - p._init = function (src, owner) { - this.src = src; - this.owner = owner; - }; - - /** - * Begin loading the content. - * #method load - * @param {String} src The path to the sound. - */ - p.load = function (src) { - if (src != null) {this.src = src;} - - this.request = new XMLHttpRequest(); - this.request.open("GET", this.src, true); - this.request.responseType = "arraybuffer"; - this.request.onload = createjs.proxy(this.handleLoad, this); - this.request.onerror = createjs.proxy(this.handleError, this); - this.request.onprogress = createjs.proxy(this.handleProgress, this); - - this.request.send(); - }; - - /** - * The loader has reported progress. - * - * <strong>Note</strong>: this is not a public API, but is used to allow preloaders to subscribe to load - * progress as if this is an HTML audio tag. This reason is why this still uses a callback instead of an event. - * #method handleProgress - * @param {event} event Progress event that gives event.loaded and event.total if server is configured correctly - * @protected - */ - p.handleProgress = function (event) { - if (!event || event.loaded > 0 && event.total == 0) { - return; // Sometimes we get no "total", so just ignore the progress event. - } - this.progress = event.loaded / event.total; - this.onprogress && this.onprogress({loaded:event.loaded, total:event.total, progress:this.progress}); - }; - - /** - * The sound has completed loading. - * #method handleLoad - * @protected - */ - p.handleLoad = function () { - this.owner.context.decodeAudioData(this.request.response, - createjs.proxy(this.handleAudioDecoded, this), - createjs.proxy(this.handleError, this)); - }; - - /** - * The audio has been decoded. - * #method handleAudioDecoded - * @protected - */ - p.handleAudioDecoded = function (decodedAudio) { - this.progress = 1; - this.result = decodedAudio; - this.owner.addPreloadResults(this.src, this.result); - this.onload && this.onload(this); - }; - - /** - * Errors have been caused by the loader. - * #method handleError - * @protected - */ - p.handleError = function (evt) { - this.owner.removeSound(this.src); - this.onerror && this.onerror(evt); - }; - - /** - * Remove all external references from loader - * #method cleanUp - */ - p.cleanUp = function () { - if(!this.request) {return;} - this.src = null; - this.owner = null; - this.request.onload = null; - this.request.onerror = null; - this.request.onprogress = null; - this.request = null; - this.onload = null; - this.onprogress = null; - this.onerror = null; - }; - - p.toString = function () { - return "[WebAudioPlugin Loader]"; - }; - - createjs.WebAudioPlugin.Loader = Loader; - -}()); -/* - * HTMLAudioPlugin - * Visit http://createjs.com/ for documentation, updates and examples. - * - * - * Copyright (c) 2012 gskinner.com, inc. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * @module SoundJS - */ - -// namespace: this.createjs = this.createjs || {}; +//TODO verify that tags no longer need to be precreated (mac and pc) +//TODO modify this now that tags do not need to be precreated (function () { - - "use strict"; - - /** - * Play sounds using HTML <audio> tags in the browser. This plugin is the second priority plugin installed - * by default, after the {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. For older browsers that do not support html - * audio, include and install the {{#crossLink "FlashPlugin"}}{{/crossLink}}. - * - * <h4>Known Browser and OS issues for HTML Audio</h4> - * <b>All browsers</b><br /> - * Testing has shown in all browsers there is a limit to how many audio tag instances you are allowed. If you exceed - * this limit, you can expect to see unpredictable results. This will be seen as soon as you register sounds, as - * tags are precreated to allow Chrome to load them. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as - * a guide to how many total audio tags you can safely use in all browsers. - * - * <b>IE html limitations</b><br /> - * <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have - * muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of - * when or how you apply the volume change, as the tag seems to need to play to apply it.</li> - * <li>MP3 encoding will not always work for audio tags if it's not default. We've found default encoding with - * 64kbps works.</li> - * <li>Occasionally very short samples will get cut off.</li> - * <li>There is a limit to how many audio tags you can load and play at once, which appears to be determined by - * hardware and browser settings. See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe estimate. - * Note that audio sprites can be used as a solution to this issue.</li></ul> - * - * <b>Safari limitations</b><br /> - * <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul> - * - * <b>iOS 6 limitations</b><br /> - * <ul><li>Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)</li> - * <li>HTML Audio is disabled by default because</li> - * <li>can only have one <audio> tag</li> - * <li>can not preload or autoplay the audio</li> - * <li>can not cache the audio</li> - * <li>can not play the audio except inside a user initiated event.</li> - * <li>audio sprites can be used to mitigate some of these issues and are strongly recommended on iOS</li> - * </ul> - * - * <b>Android Native Browser limitations</b><br /> - * <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li> - * <li>We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use a delay.</li></ul> - * <b> Android Chrome 26.0.1410.58 specific limitations</b><br /> - * <ul><li>Chrome reports true when you run createjs.Sound.BrowserDetect.isChrome, but is a different browser - * with different abilities.</li> - * <li>Can only play 1 sound at a time.</li> - * <li>Sound is not cached.</li> - * <li>Sound can only be loaded in a user initiated touch/click event.</li> - * <li>There is a delay before a sound is played, presumably while the src is loaded.</li> - * </ul> - * - * See {{#crossLink "Sound"}}{{/crossLink}} for general notes on known issues. - * - * @class HTMLAudioPlugin - * @constructor - */ - function HTMLAudioPlugin() { - this._init(); - } - - var s = HTMLAudioPlugin; - - /** - * The maximum number of instances that can be loaded and played. This is a browser limitation, primarily limited to IE9. - * The actual number varies from browser to browser (and is largely hardware dependant), but this is a safe estimate. - * @property MAX_INSTANCES - * @type {Number} - * @default 30 - * @static - */ - s.MAX_INSTANCES = 30; - - /** - * Event constant for the "canPlayThrough" event for cleaner code. - * @property _AUDIO_READY - * @type {String} - * @default canplaythrough - * @static - * @protected - */ - s._AUDIO_READY = "canplaythrough"; - - /** - * Event constant for the "ended" event for cleaner code. - * @property _AUDIO_ENDED - * @type {String} - * @default ended - * @static - * @protected - */ - s._AUDIO_ENDED = "ended"; - - /** - * Event constant for the "seeked" event for cleaner code. We utilize this event for maintaining loop events. - * @property _AUDIO_SEEKED - * @type {String} - * @default seeked - * @static - * @protected - */ - s._AUDIO_SEEKED = "seeked"; - - /** - * Event constant for the "stalled" event for cleaner code. - * @property _AUDIO_STALLED - * @type {String} - * @default stalled - * @static - * @protected - */ - s._AUDIO_STALLED = "stalled"; - - /** - * Event constant for the "timeupdate" event for cleaner code. Utilized for looping audio sprites. - * This event callsback ever 15 to 250ms and can be dropped by the browser for performance. - * @property _TIME_UPDATE - * @type {String} - * @default timeupdate - * @static - * @protected - */ - s._TIME_UPDATE = "timeupdate"; - - /** - * The capabilities of the plugin. This is generated via the the SoundInstance {{#crossLink "HTMLAudioPlugin/_generateCapabilities"}}{{/crossLink}} - * method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for an overview of all - * of the available properties. - * @property _capabilities - * @type {Object} - * @protected - * @static - */ - s._capabilities = null; - - /** - * Deprecated now that we have audio sprite support. Audio sprites are strongly recommend on iOS. - * <li>it can only have one <audio> tag</li> - * <li>can not preload or autoplay the audio</li> - * <li>can not cache the audio</li> - * <li>can not play the audio except inside a user initiated event</li> - * - * @property enableIOS - * @type {Boolean} - * @default false - * @deprecated - */ - s.enableIOS = false; - - /** - * Determine if the plugin can be used in the current browser/OS. Note that HTML audio is available in most modern - * browsers, but is disabled in iOS because of its limitations. - * @method isSupported - * @return {Boolean} If the plugin can be initialized. - * @static - */ - s.isSupported = function () { - s._generateCapabilities(); - if (s._capabilities == null) {return false;} - return true; - }; - - /** - * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} - * method for an overview of plugin capabilities. - * @method _generateCapabilities - * @static - * @protected - */ - s._generateCapabilities = function () { - if (s._capabilities != null) {return;} - var t = document.createElement("audio"); - if (t.canPlayType == null) {return null;} - - s._capabilities = { - panning:true, - volume:true, - tracks:-1 - }; - - // determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS - var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS; - var extensionMap = createjs.Sound.EXTENSION_MAP; - for (var i = 0, l = supportedExtensions.length; i < l; i++) { - var ext = supportedExtensions[i]; - var playType = extensionMap[ext] || ext; - s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != ""); - } // OJR another way to do this might be canPlayType:"m4a", codex: mp4 - } - - var p = HTMLAudioPlugin.prototype; - p.constructor = HTMLAudioPlugin; - - // doc'd above - p._capabilities = null; - - /** - * Object hash indexed by the source of each file to indicate if an audio source is loaded, or loading. - * @property _audioSources - * @type {Object} - * @protected - * @since 0.4.0 - */ - p._audioSources = null; - - /** - * The default number of instances to allow. Used by {{#crossLink "Sound"}}{{/crossLink}} when a source - * is registered using the {{#crossLink "Sound/register"}}{{/crossLink}} method. This is only used if - * a value is not provided. - * - * <b>NOTE this property only exists as a limitation of HTML audio.</b> - * @property defaultNumChannels - * @type {Number} - * @default 2 - * @since 0.4.0 - */ - p.defaultNumChannels = 2; - - /** - * An initialization function run by the constructor - * @method _init - * @protected - */ - p._init = function () { - this._capabilities = s._capabilities; - this._audioSources = {}; - }; - - /** - * Pre-register a sound instance when preloading/setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}. - * Note that this provides an object containing a tag used for preloading purposes, which - * <a href="http://preloadjs.com" target="_blank">PreloadJS</a> can use to assist with preloading. - * @method register - * @param {String} src The source of the audio - * @param {Number} instances The number of concurrently playing instances to allow for the channel at any time. - * @return {Object} A result object, containing a tag for preloading purposes and a numChannels value for internally - * controlling how many instances of a source can be played by default. - */ - p.register = function (src, instances) { - this._audioSources[src] = true; // Note this does not mean preloading has started - var channel = createjs.HTMLAudioPlugin.TagPool.get(src); - var tag = null; - var l = instances; - for (var i = 0; i < l; i++) { - tag = this._createTag(src); - channel.add(tag); - } - - return { - tag:tag // Return one instance for preloading purposes - }; - }; - - /** - * Create an HTML audio tag. - * @method _createTag - * @param {String} src The source file to set for the audio tag. - * @return {HTMLElement} Returns an HTML audio tag. - * @protected - */ - p._createTag = function (src) { - var tag = document.createElement("audio"); - tag.autoplay = false; - tag.preload = "none"; - //LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works. - tag.src = src; - return tag; - }; - - /** - * Remove a sound added using {{#crossLink "HTMLAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel - * a preload. - * @method removeSound - * @param {String} src The sound URI to unload. - * @since 0.4.1 - */ - p.removeSound = function (src) { - delete(this._audioSources[src]); - createjs.HTMLAudioPlugin.TagPool.remove(src); - }; - - /** - * Remove all sounds added using {{#crossLink "HTMLAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload. - * @method removeAllSounds - * @param {String} src The sound URI to unload. - * @since 0.4.1 - */ - p.removeAllSounds = function () { - this._audioSources = {}; - createjs.HTMLAudioPlugin.TagPool.removeAll(); - }; - - /** - * Create a sound instance. If the sound has not been preloaded, it is internally preloaded here. - * @method create - * @param {String} src The sound source to use. - * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds. - * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds. - * @return {SoundInstance} A sound instance for playback and control. - */ - p.create = function (src, startTime, duration) { - // if this sound has not be registered, create a tag and preload it - if (!this.isPreloadStarted(src)) { - var channel = createjs.HTMLAudioPlugin.TagPool.get(src); - var tag = this._createTag(src); - tag.id = src; - channel.add(tag); - this.preload(src, {tag:tag}); - } - - return new createjs.HTMLAudioPlugin.SoundInstance(src, startTime, duration, this); - }; - - /** - * Checks if preloading has started for a specific source. - * @method isPreloadStarted - * @param {String} src The sound URI to check. - * @return {Boolean} If the preload has started. - * @since 0.4.0 - */ - p.isPreloadStarted = function (src) { - return (this._audioSources[src] != null); - }; - - /** - * Internally preload a sound. - * @method preload - * @param {String} src The sound URI to load. - * @param {Object} tag An HTML audio tag used to load src. - * @since 0.4.0 - */ - p.preload = function (src, tag) { - this._audioSources[src] = true; - new createjs.HTMLAudioPlugin.Loader(src, tag); - }; - - p.toString = function () { - return "[HTMLAudioPlugin]"; - }; - - createjs.HTMLAudioPlugin = HTMLAudioPlugin; -}()); - - -(function () { - - "use strict"; - - // NOTE Documentation for the SoundInstance class in WebAudioPlugin file. Each plugin generates a SoundInstance that - // follows the same interface. - function SoundInstance(src, startTime, duration, owner) { - this._init(src, startTime, duration, owner); - } - - var p = SoundInstance.prototype = new createjs.EventDispatcher(); - p.constructor = SoundInstance; - - p.src = null; - p.uniqueId = -1; - p.playState = null; - p._owner = null; - p.loaded = false; - p._offset = 0; - p._startTime = 0; - p._volume = 1; - if (createjs.definePropertySupported) { - Object.defineProperty(p, "volume", { - get: function() { - return this._volume; - }, - set: function(value) { - if (Number(value) == null) {return;} - value = Math.max(0, Math.min(1, value)); - this._volume = value; - this._updateVolume(); - } - }); - } - p.pan = 0; - p._duration = 0; - p._audioSpriteStopTime = null; // HTMLAudioPlugin only - p._remainingLoops = 0; - if (createjs.definePropertySupported) { - Object.defineProperty(p, "loop", { - get: function() { - return this._remainingLoops; - }, - set: function(value) { - if (this.tag != null) { - // remove looping - if (this._remainingLoops != 0 && value == 0) { - this.tag.loop = false; - this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); - } - // add looping - if (this._remainingLoops == 0 && value != 0) { - this.tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); - this.tag.loop = true; - } - } - this._remainingLoops = value; - } - }); - } - p._delayTimeoutId = null; - p.tag = null; - p._muted = false; - p.paused = false; - p._paused = false; - - // Proxies, make removing listeners easier. - p._endedHandler = null; - p._readyHandler = null; - p._stalledHandler = null; - p._audioSpriteEndHandler = null; - p.loopHandler = null; - -// Constructor - p._init = function (src, startTime, duration, owner) { - this.src = src; - this._startTime = startTime || 0; // convert ms to s as web audio handles everything in seconds - if (duration) { - this._duration = duration; - this._audioSpriteStopTime = (startTime + duration) * 0.001; - } else { - this._duration = createjs.HTMLAudioPlugin.TagPool.getDuration(this.src); - } - this._owner = owner; - - this._endedHandler = createjs.proxy(this._handleSoundComplete, this); - this._readyHandler = createjs.proxy(this._handleSoundReady, this); - this._stalledHandler = createjs.proxy(this._handleSoundStalled, this); - this.__audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this); - this.loopHandler = createjs.proxy(this.handleSoundLoop, this); - }; - - p._sendEvent = function (type) { - var event = new createjs.Event(type); - this.dispatchEvent(event); - }; - - p._cleanUp = function () { - var tag = this.tag; - if (tag != null) { - tag.pause(); - this.tag.loop = false; - tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false); - tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); - tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); - tag.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this.__audioSpriteEndHandler, false); - - try { - tag.currentTime = this._startTime; - } catch (e) { - } // Reset Position - createjs.HTMLAudioPlugin.TagPool.setInstance(this.src, tag); - this.tag = null; - } - - clearTimeout(this._delayTimeoutId); - createjs.Sound._playFinished(this); - }; - - p._interrupt = function () { - if (this.tag == null) {return;} - this.playState = createjs.Sound.PLAY_INTERRUPTED; - this._cleanUp(); - this.paused = this._paused = false; - this._sendEvent("interrupted"); - }; - -// Public API - p.play = function (interrupt, delay, offset, loop, volume, pan) { - if (this.playState == createjs.Sound.PLAY_SUCCEEDED) { - if (interrupt instanceof Object) { - offset = interrupt.offset; - loop = interrupt.loop; - volume = interrupt.volume; - pan = interrupt.pan; - } - if (offset != null) { this.setPosition(offset) } - if (loop != null) { this.loop = loop; } - if (volume != null) { this.setVolume(volume); } - if (pan != null) { this.setPan(pan); } - if (this._paused) { this.resume(); } - return; - } - this._cleanUp(); - createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan); - }; - - p._beginPlaying = function (offset, loop, volume, pan) { - var tag = this.tag = createjs.HTMLAudioPlugin.TagPool.getInstance(this.src); - if (tag == null) { - this.playFailed(); - return -1; - } - - tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false); - - // Reset this instance. - this._offset = offset; - this.volume = volume; - this._updateVolume(); - this._remainingLoops = loop; - - if (tag.readyState !== 4) { - tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); - tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false); - tag.preload = "auto"; // This is necessary for Firefox, as it won't ever "load" until this is set. - tag.load(); - } else { - this._handleSoundReady(null); - } - - this._sendEvent("succeeded"); - return 1; - }; - - // Note: Sounds stall when trying to begin playback of a new audio instance when the existing instances - // has not loaded yet. This doesn't mean the sound will not play. - p._handleSoundStalled = function (event) { - this._cleanUp(); // OJR this will stop playback, we could remove this and let the developer decide how to handle stalled instances - this._sendEvent("failed"); - }; - - p._handleSoundReady = function (event) { - this.playState = createjs.Sound.PLAY_SUCCEEDED; - this.paused = this._paused = false; - this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); - this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); - - if (this._offset >= this.getDuration()) { - this.playFailed(); - return; - } - this.tag.currentTime = (this._startTime + this._offset) * 0.001; - - if (this._audioSpriteStopTime) { - this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false); - this.tag.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this.__audioSpriteEndHandler, false); - } else { - if(this._remainingLoops != 0) { - this.tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); - this.tag.loop = true; - } - } - - this.tag.play(); - }; - - p.pause = function () { - if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED && this.tag != null) { - this.paused = this._paused = true; - this.tag.pause(); - clearTimeout(this._delayTimeoutId); - return true; - } - return false; - }; - - p.resume = function () { - if (!this._paused || this.tag == null) {return false;} - this.paused = this._paused = false; - this.tag.play(); - return true; - }; - - p.stop = function () { - this._offset = 0; - this.pause(); - this.playState = createjs.Sound.PLAY_FINISHED; - this._cleanUp(); - return true; - }; - - p.setMasterVolume = function (value) { - this._updateVolume(); - }; - - p.setVolume = function (value) { - this.volume = value; - return true; - }; - - p._updateVolume = function () { - if (this.tag != null) { - var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume; - if (newVolume != this.tag.volume) {this.tag.volume = newVolume;} - } - }; - - p.getVolume = function (value) { - return this.volume; - }; - - p.setMasterMute = function (isMuted) { - this._updateVolume(); - }; - - p.setMute = function (isMuted) { - if (isMuted == null) {return false;} - this._muted = isMuted; - this._updateVolume(); - return true; - }; - - p.getMute = function () { - return this._muted; - }; - - // Can not set pan in HTML audio - p.setPan = function (value) { - return false; - }; - - p.getPan = function () { - return 0; - }; - - p.getPosition = function () { - if (this.tag == null) {return this._offset;} - return (this.tag.currentTime * 1000) - this._startTime; - }; - - p.setPosition = function (value) { - if (this.tag == null) { - this._offset = value - } else { - this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); - this.tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false); - try { - value = value + this._startTime; - this.tag.currentTime = value * 0.001; - } catch (error) { // Out of range - this._handleSetPositionSeek(null); - return false; - } - } - return true; - }; - - p._handleSetPositionSeek = function(event) { - if (this.tag == null) { return; } - this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false); - this.tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); - }; - - p.getDuration = function () { // NOTE this will always return 0 until sound has been played unless it is set - return this._duration; - }; - - p._handleSoundComplete = function (event) { - this._offset = 0; - this.playState = createjs.Sound.PLAY_FINISHED; - this._cleanUp(); - this._sendEvent("complete"); - }; - - // NOTE because of the inaccuracies in the timeupdate event (15 - 250ms) and in setting the tag to the desired timed - // (up to 300ms), it is strongly recommended not to loop audio sprites with HTML Audio if smooth looping is desired - p._handleAudioSpriteLoop = function (event) { - if(this.tag.currentTime <= this._audioSpriteStopTime) {return;} - this.tag.pause(); - if(this._remainingLoops == 0) { - this._handleSoundComplete(null); - } else { - this._offset = 0; - this._remainingLoops--; - this.tag.currentTime = this._startTime * 0.001; - if(!this._paused) {this.tag.play();} - this._sendEvent("loop"); - } - }; - - // NOTE with this approach audio will loop as reliably as the browser allows - // but we could end up sending the loop event after next loop playback begins - p.handleSoundLoop = function (event) { - this._offset = 0; - this._remainingLoops--; - if(this._remainingLoops == 0) { - this.tag.loop = false; - this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); - } - this._sendEvent("loop"); - }; - - p.playFailed = function () { - this.playState = createjs.Sound.PLAY_FAILED; - this._cleanUp(); - this._sendEvent("failed"); - }; - - p.toString = function () { - return "[HTMLAudioPlugin SoundInstance]"; - }; - - createjs.HTMLAudioPlugin.SoundInstance = SoundInstance; - -}()); - - -(function () { - - "use strict"; - - /** - * An internal helper class that preloads html audio via HTMLAudioElement tags. Note that PreloadJS will NOT use - * this load class like it does Flash and WebAudio plugins. - * Note that this class and its methods are not documented properly to avoid generating HTML documentation. - * #class Loader - * @param {String} src The source of the sound to load. - * @param {HTMLAudioElement} tag The audio tag of the sound to load. - * @constructor - * @protected - * @since 0.4.0 - */ - function Loader(src, tag) { - this._init(src, tag); - }; - - var p = Loader.prototype; - p.constructor = Loader; - - /** - * The source to be loaded. - * #property src - * @type {String} - * @default null - * @protected - */ - p.src = null; - - /** - * The tag to load the source with / into. - * #property tag - * @type {AudioTag} - * @default null - * @protected - */ - p.tag = null; - - /** - * An interval used to give us progress. - * #property preloadTimer - * @type {String} - * @default null - * @protected - */ - p.preloadTimer = null; - - // Proxies, make removing listeners easier. - p.loadedHandler = null; - - // constructor - p._init = function (src, tag) { - this.src = src; - this.tag = tag; - - this.preloadTimer = setInterval(createjs.proxy(this.preloadTick, this), 200); - - // This will tell us when audio is buffered enough to play through, but not when its loaded. - // The tag doesn't keep loading in Chrome once enough has buffered, and we have decided that behaviour is sufficient. - // Note that canplaythrough callback doesn't work in Chrome, we have to use the event. - this.loadedHandler = createjs.proxy(this.sendLoadedEvent, this); // we need this bind to be able to remove event listeners - this.tag.addEventListener && this.tag.addEventListener("canplaythrough", this.loadedHandler); - if(this.tag.onreadystatechange == null) { - this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this); - } else { - var f = this.tag.onreadystatechange; - this.tag.onreadystatechange = function() { - f(); - this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this); - } - } - - this.tag.preload = "auto"; - //this.tag.src = src; - this.tag.load(); - }; - - /** - * Allows us to have preloading progress and tell when its done. - * #method preloadTick - * @protected - */ - p.preloadTick = function () { - var buffered = this.tag.buffered; - var duration = this.tag.duration; - - if (buffered.length > 0) { - if (buffered.end(0) >= duration - 1) { - this.handleTagLoaded(); - } - } - }; - - /** - * Internal handler for when a tag is loaded. - * #method handleTagLoaded - * @protected - */ - p.handleTagLoaded = function () { - clearInterval(this.preloadTimer); - }; - - /** - * Communicates back to Sound that a load is complete. - * #method sendLoadedEvent - * @param {Object} evt The load Event - */ - p.sendLoadedEvent = function (evt) { - this.tag.removeEventListener && this.tag.removeEventListener("canplaythrough", this.loadedHandler); // cleanup and so we don't send the event more than once - this.tag.onreadystatechange = null; // cleanup and so we don't send the event more than once - createjs.Sound._sendFileLoadEvent(this.src); // fire event or callback on Sound - - }; - - // used for debugging - p.toString = function () { - return "[HTMLAudioPlugin Loader]"; - }; - - createjs.HTMLAudioPlugin.Loader = Loader; - -}()); - - -(function () { - "use strict"; /** * The TagPool is an object pool for HTMLAudio tag instances. In Chrome, we have to pre-create the number of HTML * audio tag instances that we are going to play before we load the data, otherwise the audio stalls. * (Note: This seems to be a bug in Chrome) - * #class TagPool + * @class HTMLAudioTagPool * @param {String} src The source of the channel. * @protected */ function TagPool(src) { - this._init(src); - } + +//Public Properties + /** + * The source of the tag pool. + * #property src + * @type {String} + * @protected + */ + this.src = src; + + /** + * The total number of HTMLAudio tags in this pool. This is the maximum number of instance of a certain sound + * that can play at one time. + * #property length + * @type {Number} + * @default 0 + * @protected + */ + this.length = 0; + + /** + * The number of unused HTMLAudio tags. + * #property available + * @type {Number} + * @default 0 + * @protected + */ + this.available = 0; + + /** + * A list of all available tags in the pool. + * #property tags + * @type {Array} + * @protected + */ + this.tags = []; + + /** + * The duration property of all audio tags, converted to milliseconds, which originally is only available on the + * last tag in the tags array because that is the one that is loaded. + * #property + * @type {Number} + * @protected + */ + this.duration = 0; + }; + + var p = TagPool.prototype; + p.constructor = TagPool; var s = TagPool; + +// Static Properties /** * A hash lookup of each sound channel, indexed by the audio source. * #property tags @@ -4974,6 +6614,8 @@ this.createjs = this.createjs || {}; */ s.tags = {}; + +// Static Methods /** * Get a tag pool. If the pool doesn't exist, create it. * #method get @@ -5004,18 +6646,6 @@ this.createjs = this.createjs || {}; return true; }; - /** - * Delete all TagPools and all related tags. - * #method removeAll - * @static - */ - s.removeAll = function () { - for(var channel in s.tags) { - s.tags[channel].removeAll(); // this stops and removes all active instances - } - s.tags = {}; - }; - /** * Get a tag instance. This is a shortcut method. * #method getInstance @@ -5055,59 +6685,8 @@ this.createjs = this.createjs || {}; return channel.getDuration(); }; - var p = TagPool.prototype; - p.constructor = TagPool; - - /** - * The source of the tag pool. - * #property src - * @type {String} - * @protected - */ - p.src = null; - - /** - * The total number of HTMLAudio tags in this pool. This is the maximum number of instance of a certain sound - * that can play at one time. - * #property length - * @type {Number} - * @default 0 - * @protected - */ - p.length = 0; - - /** - * The number of unused HTMLAudio tags. - * #property available - * @type {Number} - * @default 0 - * @protected - */ - p.available = 0; - - /** - * A list of all available tags in the pool. - * #property tags - * @type {Array} - * @protected - */ - p.tags = null; - - /** - * The duration property of all audio tags, converted to milliseconds, which originally is only available on the - * last tag in the tags array because that is the one that is loaded. - * #property - * @type {Number} - * @protected - */ - p.duration = 0; - - // constructor - p._init = function (src) { - this.src = src; - this.tags = []; - }; +// Public Methods /** * Add an HTMLAudio tag into the pool. * #method add @@ -5172,9 +6751,522 @@ this.createjs = this.createjs || {}; }; p.toString = function () { - return "[HTMLAudioPlugin TagPool]"; + return "[HTMLAudioTagPool]"; }; - createjs.HTMLAudioPlugin.TagPool = TagPool; - + createjs.HTMLAudioTagPool = TagPool; }()); + +//############################################################################## +// HTMLAudioSoundInstance.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + /** + * HTMLAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by + * {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}. + * + * @param {String} src The path to and file name of the sound. + * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds. + * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds. + * @param {Object} playbackResource Any resource needed by plugin to support audio playback. + * @class HTMLAudioSoundInstance + * @extends AbstractSoundInstance + * @constructor + */ + function HTMLAudioSoundInstance(src, startTime, duration, playbackResource) { + this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource); + + +// Private Properties + this._audioSpriteStopTime = null; + this._delayTimeoutId = null; + + // Proxies, make removing listeners easier. + this._endedHandler = createjs.proxy(this._handleSoundComplete, this); + this._readyHandler = createjs.proxy(this._handleTagReady, this); + this._stalledHandler = createjs.proxy(this.playFailed, this); + this._audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this); + this._loopHandler = createjs.proxy(this._handleSoundComplete, this); + + if (duration) { + this._audioSpriteStopTime = (startTime + duration) * 0.001; + } else { + this._duration = createjs.HTMLAudioTagPool.getDuration(this.src); + } + } + var p = createjs.extend(HTMLAudioSoundInstance, createjs.AbstractSoundInstance); + + +// Public Methods + /** + * Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master volume. + * undoc'd because it is not meant to be used outside of Sound + * #method setMasterVolume + * @param value + */ + p.setMasterVolume = function (value) { + this._updateVolume(); + }; + + /** + * Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master mute. + * undoc'd because it is not meant to be used outside of Sound + * #method setMasterMute + * @param value + */ + p.setMasterMute = function (isMuted) { + this._updateVolume(); + }; + + p.toString = function () { + return "[HTMLAudioSoundInstance]"; + }; + +//Private Methods + p._removeLooping = function() { + if(this._playbackResource == null) {return;} + this._playbackResource.loop = false; + this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false); + }; + + p._addLooping = function() { + if(this._playbackResource == null || this._audioSpriteStopTime) {return;} + this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false); + this._playbackResource.loop = true; + }; + + p._handleCleanUp = function () { + var tag = this._playbackResource; + if (tag != null) { + tag.pause(); + tag.loop = false; + tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false); + tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); + tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false); + tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false); + tag.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false); + + try { + tag.currentTime = this._startTime; + } catch (e) { + } // Reset Position + createjs.HTMLAudioTagPool.setInstance(this.src, tag); + this._playbackResource = null; + } + }; + + p._beginPlaying = function (offset, loop, volume, pan) { + this._playbackResource = createjs.HTMLAudioTagPool.getInstance(this.src); + return this.AbstractSoundInstance__beginPlaying(offset, loop, volume, pan); + }; + + p._handleSoundReady = function (event) { + if (this._playbackResource.readyState !== 4) { + var tag = this._playbackResource; + tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); + tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false); + tag.preload = "auto"; // This is necessary for Firefox, as it won't ever "load" until this is set. + tag.load(); + return; + } + + this._updateVolume(); + this._playbackResource.currentTime = (this._startTime + this._position) * 0.001; + if (this._audioSpriteStopTime) { + this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false); + } else { + this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false); + if(this._loop != 0) { + this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false); + this._playbackResource.loop = true; + } + } + + this._playbackResource.play(); + }; + + /** + * Used to handle when a tag is not ready for immediate playback when it is returned from the HTMLAudioTagPool. + * @method _handleTagReady + * @param event + * @protected + */ + p._handleTagReady = function (event) { + this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); + this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false); + + this._handleSoundReady(); + }; + + p._pause = function () { + this._playbackResource.pause(); + }; + + p._resume = function () { + this._playbackResource.play(); + }; + + p._updateVolume = function () { + if (this._playbackResource != null) { + var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume; + if (newVolume != this._playbackResource.volume) {this._playbackResource.volume = newVolume;} + } + }; + + p._calculateCurrentPosition = function() { + return (this._playbackResource.currentTime * 1000) - this._startTime; + }; + + p._updatePosition = function() { + this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false); + this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false); + try { + this._playbackResource.currentTime = (this._position + this._startTime) * 0.001; + } catch (error) { // Out of range + this._handleSetPositionSeek(null); + } + }; + + /** + * Used to enable setting position, as we need to wait for that seek to be done before we add back our loop handling seek listener + * @method _handleSetPositionSeek + * @param event + * @protected + */ + p._handleSetPositionSeek = function(event) { + if (this._playbackResource == null) { return; } + this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false); + this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false); + }; + + /** + * Timer used to loop audio sprites. + * NOTE because of the inaccuracies in the timeupdate event (15 - 250ms) and in setting the tag to the desired timed + * (up to 300ms), it is strongly recommended not to loop audio sprites with HTML Audio if smooth looping is desired + * + * @method _handleAudioSpriteLoop + * @param event + * @private + */ + p._handleAudioSpriteLoop = function (event) { + if(this._playbackResource.currentTime <= this._audioSpriteStopTime) {return;} + this._playbackResource.pause(); + if(this._loop == 0) { + this._handleSoundComplete(null); + } else { + this._position = 0; + this._loop--; + this._playbackResource.currentTime = this._startTime * 0.001; + if(!this._paused) {this._playbackResource.play();} + this._sendEvent("loop"); + } + }; + + // NOTE with this approach audio will loop as reliably as the browser allows + // but we could end up sending the loop event after next loop playback begins + p._handleLoop = function (event) { + if(this._loop == 0) { + this._playbackResource.loop = false; + this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false); + } + }; + + p._updateDuration = function () { + this._audioSpriteStopTime = (startTime + duration) * 0.001; + + if(this.playState == createjs.Sound.PLAY_SUCCEEDED) { + this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false); + this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false); + } + }; + + /* This should never change + p._setDurationFromSource = function () { + this._duration = createjs.HTMLAudioTagPool.getDuration(this.src); + }; + */ + + createjs.HTMLAudioSoundInstance = createjs.promote(HTMLAudioSoundInstance, "AbstractSoundInstance"); +}()); + +//############################################################################## +// HTMLAudioPlugin.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + + "use strict"; + + /** + * Play sounds using HTML <audio> tags in the browser. This plugin is the second priority plugin installed + * by default, after the {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. For older browsers that do not support html + * audio, include and install the {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}. + * + * <h4>Known Browser and OS issues for HTML Audio</h4> + * <b>All browsers</b><br /> + * Testing has shown in all browsers there is a limit to how many audio tag instances you are allowed. If you exceed + * this limit, you can expect to see unpredictable results. This will be seen as soon as you register sounds, as + * tags are precreated to allow Chrome to load them. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as + * a guide to how many total audio tags you can safely use in all browsers. + * + * <b>IE html limitations</b><br /> + * <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have + * muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of + * when or how you apply the volume change, as the tag seems to need to play to apply it.</li> + * <li>MP3 encoding will not always work for audio tags if it's not default. We've found default encoding with + * 64kbps works.</li> + * <li>Occasionally very short samples will get cut off.</li> + * <li>There is a limit to how many audio tags you can load and play at once, which appears to be determined by + * hardware and browser settings. See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe estimate. + * Note that audio sprites can be used as a solution to this issue.</li></ul> + * + * <b>Safari limitations</b><br /> + * <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul> + * + * <b>iOS 6 limitations</b><br /> + * <ul><li>Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)</li> + * <li>HTML Audio is disabled by default because</li> + * <li>can only have one <audio> tag</li> + * <li>can not preload or autoplay the audio</li> + * <li>can not cache the audio</li> + * <li>can not play the audio except inside a user initiated event.</li> + * <li>audio sprites can be used to mitigate some of these issues and are strongly recommended on iOS</li> + * </ul> + * + * <b>Android Native Browser limitations</b><br /> + * <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li> + * <li>We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use a delay.</li></ul> + * <b> Android Chrome 26.0.1410.58 specific limitations</b><br /> + * <ul> <li>Can only play 1 sound at a time.</li> + * <li>Sound is not cached.</li> + * <li>Sound can only be loaded in a user initiated touch/click event.</li> + * <li>There is a delay before a sound is played, presumably while the src is loaded.</li> + * </ul> + * + * See {{#crossLink "Sound"}}{{/crossLink}} for general notes on known issues. + * + * @class HTMLAudioPlugin + * @extends AbstractPlugin + * @constructor + */ + function HTMLAudioPlugin() { + this.AbstractPlugin_constructor(); + + + // Public Properties + /** + * The default number of instances to allow. Used by {{#crossLink "Sound"}}{{/crossLink}} when a source + * is registered using the {{#crossLink "Sound/register"}}{{/crossLink}} method. This is only used if + * a value is not provided. + * + * <b>NOTE this property only exists as a limitation of HTML audio.</b> + * @property defaultNumChannels + * @type {Number} + * @default 2 + * @since 0.4.0 + */ + this.defaultNumChannels = 2; + + this._capabilities = s._capabilities; + + this._loaderClass = createjs.SoundLoader; + this._soundInstanceClass = createjs.HTMLAudioSoundInstance; + } + + var p = createjs.extend(HTMLAudioPlugin, createjs.AbstractPlugin); + var s = HTMLAudioPlugin; + + +// Static Properties + /** + * The maximum number of instances that can be loaded and played. This is a browser limitation, primarily limited to IE9. + * The actual number varies from browser to browser (and is largely hardware dependant), but this is a safe estimate. + * Audio sprites work around this limitation. + * @property MAX_INSTANCES + * @type {Number} + * @default 30 + * @static + */ + s.MAX_INSTANCES = 30; + + /** + * Event constant for the "canPlayThrough" event for cleaner code. + * @property _AUDIO_READY + * @type {String} + * @default canplaythrough + * @static + * @protected + */ + s._AUDIO_READY = "canplaythrough"; + + /** + * Event constant for the "ended" event for cleaner code. + * @property _AUDIO_ENDED + * @type {String} + * @default ended + * @static + * @protected + */ + s._AUDIO_ENDED = "ended"; + + /** + * Event constant for the "seeked" event for cleaner code. We utilize this event for maintaining loop events. + * @property _AUDIO_SEEKED + * @type {String} + * @default seeked + * @static + * @protected + */ + s._AUDIO_SEEKED = "seeked"; + + /** + * Event constant for the "stalled" event for cleaner code. + * @property _AUDIO_STALLED + * @type {String} + * @default stalled + * @static + * @protected + */ + s._AUDIO_STALLED = "stalled"; + + /** + * Event constant for the "timeupdate" event for cleaner code. Utilized for looping audio sprites. + * This event callsback ever 15 to 250ms and can be dropped by the browser for performance. + * @property _TIME_UPDATE + * @type {String} + * @default timeupdate + * @static + * @protected + */ + s._TIME_UPDATE = "timeupdate"; + + /** + * The capabilities of the plugin. This is generated via the {{#crossLink "HTMLAudioPlugin/_generateCapabilities"}}{{/crossLink}} + * method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for an overview of all + * of the available properties. + * @property _capabilities + * @type {Object} + * @protected + * @static + */ + s._capabilities = null; + + /** + * Deprecated now that we have audio sprite support. Audio sprites are strongly recommend on iOS for the following reasons: + * <li>it can only have one <audio> tag</li> + * <li>can not preload or autoplay the audio</li> + * <li>can not cache the audio</li> + * <li>can not play the audio except inside a user initiated event</li> + * + * @property enableIOS + * @type {Boolean} + * @default false + * @deprecated + */ + s.enableIOS = false; + + +// Static Methods + /** + * Determine if the plugin can be used in the current browser/OS. Note that HTML audio is available in most modern + * browsers, but is disabled in iOS because of its limitations. + * @method isSupported + * @return {Boolean} If the plugin can be initialized. + * @static + */ + s.isSupported = function () { + s._generateCapabilities(); + if (s._capabilities == null) {return false;} + return true; + }; + + /** + * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} + * method for an overview of plugin capabilities. + * @method _generateCapabilities + * @static + * @protected + */ + s._generateCapabilities = function () { + if (s._capabilities != null) {return;} + var t = document.createElement("audio"); + if (t.canPlayType == null) {return null;} + + s._capabilities = { + panning:true, + volume:true, + tracks:-1 + }; + + // determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS + var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS; + var extensionMap = createjs.Sound.EXTENSION_MAP; + for (var i = 0, l = supportedExtensions.length; i < l; i++) { + var ext = supportedExtensions[i]; + var playType = extensionMap[ext] || ext; + s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != ""); + } // OJR another way to do this might be canPlayType:"m4a", codex: mp4 + }; + + +// public methods + p.register = function (src, instances) { + var channel = createjs.HTMLAudioTagPool.get(src); + var tag = null; + for (var i = 0; i < instances; i++) { + tag = this._createTag(src); + channel.add(tag); + } + + var loader = this.AbstractPlugin_register(src, instances); + loader.setTag(tag); + + return loader; + }; + + p.removeSound = function (src) { + this.AbstractPlugin_removeSound(src); + createjs.HTMLAudioTagPool.remove(src); + }; + + p.create = function (src, startTime, duration) { + var si = this.AbstractPlugin_create(src, startTime, duration); + si.setPlaybackResource(null); + return si; + }; + + p.toString = function () { + return "[HTMLAudioPlugin]"; + }; + + // plugin does not support these + p.setVolume = p.getVolume = p.setMute = null; + + +// private methods + /** + * Create an HTML audio tag. + * @method _createTag + * @param {String} src The source file to set for the audio tag. + * @return {HTMLElement} Returns an HTML audio tag. + * @protected + */ + // TODO move this to tagpool when it changes to be a standard object pool + p._createTag = function (src) { + var tag = document.createElement("audio"); + tag.autoplay = false; + tag.preload = "none"; + //LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works. + tag.src = src; + return tag; + }; + + createjs.HTMLAudioPlugin = createjs.promote(HTMLAudioPlugin, "AbstractPlugin"); +}()); \ No newline at end of file