@windward/games 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/components/content/DatableEditor.vue +0 -3
  2. package/components/content/blocks/crosswordPuzzle/Crossword.ts +553 -0
  3. package/components/content/blocks/crosswordPuzzle/CrosswordClues.vue +91 -0
  4. package/components/content/blocks/crosswordPuzzle/CrosswordElements.ts +38 -0
  5. package/components/content/blocks/crosswordPuzzle/CrosswordPuzzle.vue +673 -0
  6. package/components/content/blocks/multipleChoice/MultipleChoice.vue +187 -127
  7. package/components/content/blocks/multipleChoice/QuestionDialog.vue +37 -13
  8. package/components/content/blocks/sevenStrikes/SevenStikes.vue +368 -0
  9. package/components/content/blocks/sevenStrikes/keyboard.vue +71 -0
  10. package/components/content/blocks/wordJumble/Jumble.vue +2 -10
  11. package/components/content/blocks/wordJumble/WordJumble.vue +22 -10
  12. package/components/settings/CrosswordPuzzleSettingsManager.vue +271 -0
  13. package/components/settings/MultipleChoiceSettingsManager.vue +21 -7
  14. package/components/settings/SevenStrikesSettingsManager.vue +288 -0
  15. package/components/settings/WordJumbleSettingsManager.vue +4 -1
  16. package/i18n/en-US/components/content/blocks/crossword.ts +22 -0
  17. package/i18n/en-US/components/content/blocks/index.ts +4 -0
  18. package/i18n/en-US/components/content/blocks/multiple_choice.ts +2 -4
  19. package/i18n/en-US/components/content/blocks/seven_strikes.ts +6 -0
  20. package/i18n/en-US/components/settings/crossword.ts +7 -0
  21. package/i18n/en-US/components/settings/index.ts +4 -0
  22. package/i18n/en-US/components/settings/multiple_choice.ts +1 -1
  23. package/i18n/en-US/components/settings/seven_strikes.ts +8 -0
  24. package/i18n/en-US/components/settings/word_jumble.ts +1 -1
  25. package/i18n/en-US/shared/content_blocks.ts +2 -0
  26. package/i18n/en-US/shared/settings.ts +2 -0
  27. package/package.json +2 -1
  28. package/plugin.js +43 -1
  29. package/test/blocks/crossword/CrosswordPuzzle.spec.js +49 -0
  30. package/test/blocks/sevenStrikes/sevenStrikes.spec.js +24 -0
  31. package/test/settings/BucketGameManager.spec.js +1 -1
  32. package/test/settings/CrosswordPuzzleManager.spec.js +103 -0
  33. package/test/settings/SevenStrikesManager.spec.js +53 -0
  34. package/test/settings/WordJumbleManager.spec.js +2 -2
@@ -0,0 +1,271 @@
1
+ <template>
2
+ <div>
3
+ <v-form ref="form" v-model="valid">
4
+ <v-text-field
5
+ v-model="block.metadata.config.title"
6
+ :counter="50"
7
+ :label="
8
+ $t(
9
+ 'plugin.games.components.settings.bucket_game.form.title'
10
+ )
11
+ "
12
+ ref="title"
13
+ ></v-text-field>
14
+ <br />
15
+ <v-textarea
16
+ outlined
17
+ auto-grow
18
+ v-model="block.metadata.config.instructions"
19
+ :counter="255"
20
+ :label="
21
+ $t(
22
+ 'plugin.games.components.settings.bucket_game.form.instructions'
23
+ )
24
+ "
25
+ ></v-textarea>
26
+ <v-expansion-panels flat>
27
+ <v-expansion-panel>
28
+ <v-expansion-panel-header class="pa-0"
29
+ ><h3>
30
+ {{
31
+ $t(
32
+ 'plugin.games.components.settings.crossword.crossword_items'
33
+ )
34
+ }}
35
+ </h3></v-expansion-panel-header
36
+ >
37
+ <v-expansion-panel-content class="ma-0">
38
+ <v-container class="pa-0">
39
+ <p>
40
+ {{
41
+ $t(
42
+ 'plugin.games.components.settings.crossword.min_length'
43
+ )
44
+ }}
45
+ </p>
46
+ <v-card
47
+ v-for="(item, index) in block.metadata.config
48
+ .words"
49
+ :key="'bucket_' + index"
50
+ class="pa-2 flex-fill cardOutline"
51
+ elevation="0"
52
+ outlined
53
+ tile
54
+ >
55
+ <v-row class="itemHeight">
56
+ <p
57
+ class="text-truncate pa-0 ma-0 seventy"
58
+ @click="onOpenEditor(index)"
59
+ v-on:keyup.enter="onOpenEditor(index)"
60
+ @mouseover="onHover"
61
+ @mouseleave="onHoverLeave"
62
+ :class="cursor"
63
+ tabindex="0"
64
+ >
65
+ <v-icon class="pl-2"
66
+ >mdi-gesture-tap-hold</v-icon
67
+ >
68
+ {{
69
+ item.word
70
+ ? item.word
71
+ : $t(
72
+ 'plugin.games.components.settings.bucket_game.form.enter_text'
73
+ )
74
+ }}
75
+ </p>
76
+ <v-icon
77
+ class="twenty"
78
+ @click="onDelete(index)"
79
+ >mdi-delete-outline</v-icon
80
+ >
81
+ </v-row>
82
+ <v-textarea
83
+ v-if="item.expand"
84
+ outlined
85
+ :counter="20"
86
+ maxlength="20"
87
+ :autofocus="true"
88
+ :label="
89
+ $t(
90
+ 'plugin.games.components.settings.crossword.word'
91
+ )
92
+ "
93
+ class="pt-4"
94
+ v-model="
95
+ block.metadata.config.words[index].word
96
+ "
97
+ ></v-textarea>
98
+ <v-textarea
99
+ v-if="item.expand"
100
+ outlined
101
+ :counter="155"
102
+ maxlength="155"
103
+ :label="
104
+ $t(
105
+ 'plugin.games.components.settings.crossword.clue'
106
+ )
107
+ "
108
+ class="pt-4"
109
+ v-model="
110
+ block.metadata.config.words[index].clue
111
+ "
112
+ ></v-textarea>
113
+ </v-card>
114
+ <p
115
+ @mouseover="onHover"
116
+ @mouseleave="onHoverLeave"
117
+ @click="onAddElement"
118
+ v-on:keyup.enter="onAddElement"
119
+ class="fullWidth"
120
+ :class="cursor"
121
+ tabindex="0"
122
+ >
123
+ <v-icon class="primary addIcon"
124
+ >mdi-plus</v-icon
125
+ >
126
+ {{
127
+ $t(
128
+ 'plugin.games.components.content.blocks.crossword.add_element'
129
+ )
130
+ }}
131
+ </p>
132
+ </v-container>
133
+ </v-expansion-panel-content>
134
+ </v-expansion-panel>
135
+ </v-expansion-panels>
136
+ </v-form>
137
+ </div>
138
+ </template>
139
+
140
+ <script>
141
+ import _ from 'lodash'
142
+ import BaseContentSettings from '~/components/Content/Tool/BaseContentSettings.js'
143
+ export default {
144
+ name: 'CrosswordPuzzleSettingsManager',
145
+ extends: BaseContentSettings,
146
+ components: {},
147
+ beforeMount() {
148
+ if (_.isEmpty(this.block)) {
149
+ this.block = {}
150
+ }
151
+ if (_.isEmpty(this.block.metadata)) {
152
+ this.block.metadata = {}
153
+ }
154
+ if (_.isEmpty(this.block.metadata.config)) {
155
+ this.block.metadata.config = {}
156
+ }
157
+ if (_.isEmpty(this.block.metadata.config.title)) {
158
+ this.block.metadata.config.title = ''
159
+ }
160
+ if (_.isEmpty(this.block.metadata.config.instructions)) {
161
+ this.block.metadata.config.instructions = ''
162
+ }
163
+ if (_.isEmpty(this.block.metadata.config.words)) {
164
+ this.block.metadata.config.words = []
165
+ let counter = 0
166
+ // ensures that there will always be at least two crossword elements to edit
167
+ while (counter < 2) {
168
+ const default_object = {
169
+ id: counter,
170
+ word: '',
171
+ clue: '',
172
+ expand: false,
173
+ }
174
+ counter = counter + 1
175
+ !this.block.metadata.config.words.push(default_object)
176
+ }
177
+ }
178
+ },
179
+ data() {
180
+ return {
181
+ valid: true,
182
+ cursor: null,
183
+ }
184
+ },
185
+ methods: {
186
+ onAddElement() {
187
+ // closes all elements that were expanded
188
+ this.block.metadata.config.words.forEach((element) => {
189
+ element.expand = false
190
+ })
191
+ // pushes in new crossword object and expands element
192
+ const default_object = {
193
+ id: this.block.metadata.config.words.length,
194
+ word: '',
195
+ clue: '',
196
+ expand: true,
197
+ }
198
+ this.block.metadata.config.words.push(default_object)
199
+ },
200
+ onDelete(index) {
201
+ this.block.metadata.config.words.splice(index, 1)
202
+ let counter = 0
203
+ this.block.metadata.config.words.forEach((element) => {
204
+ element.id = counter
205
+ counter = counter + 1
206
+ })
207
+ if (this.block.metadata.config.words.length < 2) {
208
+ this.onAddElement()
209
+ }
210
+ },
211
+ onOpenEditor(index) {
212
+ this.block.metadata.config.words[index].expand =
213
+ !this.block.metadata.config.words[index].expand
214
+ this.block.metadata.config.words.forEach((element) => {
215
+ if (index !== element.id) {
216
+ element.expand = false
217
+ }
218
+ })
219
+ },
220
+ onHover(index) {
221
+ this.cursor = 'changePointer'
222
+ },
223
+ onHoverLeave() {
224
+ this.cursor = ''
225
+ },
226
+ },
227
+ }
228
+ </script>
229
+ <style lang="scss" scoped>
230
+ .v-progress-circular {
231
+ margin: 1rem;
232
+ }
233
+ .changePointer {
234
+ cursor: pointer !important;
235
+ }
236
+ .cardOutline {
237
+ border: 2px solid var(--v-primary-base);
238
+ margin-bottom: 8px;
239
+ }
240
+ .fullWidth {
241
+ display: flex;
242
+ justify-content: center;
243
+ align-items: center;
244
+ font-weight: bold;
245
+ font-size: 16px;
246
+ }
247
+ .itemHeight {
248
+ display: flex;
249
+ align-content: center;
250
+ justify-content: space-between;
251
+ height: 60px;
252
+ }
253
+ .seventy {
254
+ width: 70%;
255
+ height: 100%;
256
+ display: flex;
257
+ align-items: center;
258
+ }
259
+ .twenty {
260
+ width: 20%;
261
+ height: 90%;
262
+ margin-top: 3px;
263
+ border-left: 2px solid var(--v-primary-base);
264
+ }
265
+ .addIcon {
266
+ margin-right: 5px;
267
+ }
268
+ .v-expansion-panel-content::v-deep .v-expansion-panel-content__wrap {
269
+ padding: 0 !important;
270
+ }
271
+ </style>
@@ -28,13 +28,13 @@
28
28
  <v-expansion-panels flat>
29
29
  <v-expansion-panel>
30
30
  <v-expansion-panel-header class="pa-0">
31
- <h3>
31
+ <h5>
32
32
  {{
33
33
  $t(
34
34
  'plugin.games.components.settings.multiple_choice.questions'
35
35
  )
36
36
  }}
37
- </h3></v-expansion-panel-header
37
+ </h5></v-expansion-panel-header
38
38
  >
39
39
  <v-expansion-panel-content class="ma-0">
40
40
  <v-container class="pa-0">
@@ -96,7 +96,7 @@
96
96
  @mouseover="onHover"
97
97
  @mouseleave="onHoverLeave"
98
98
  @click="onOpenModal"
99
- v-on:keyup.enter="onOpenModal"
99
+ v-on:keydown.enter="onOpenModal"
100
100
  class="fullWidth"
101
101
  :class="cursor"
102
102
  tabindex="0"
@@ -149,6 +149,7 @@ import draggable from 'vuedraggable'
149
149
  import _ from 'lodash'
150
150
  import Dialog from '~/components/Dialog.vue'
151
151
  import QuestionDialog from '../content/blocks/multipleChoice/QuestionDialog.vue'
152
+ import Crypto from '~/helpers/Crypto'
152
153
 
153
154
  export default {
154
155
  name: 'MultipleChoiceSettingsManager',
@@ -195,24 +196,32 @@ export default {
195
196
  // when left the saveAndNew button it was causing errors
196
197
  this.isNotEditing = false
197
198
  } else {
199
+ let firstAnswerId = Crypto.id()
198
200
  this.block.metadata.config.questions.push({
201
+ // crypto.id for all ids and then use filters
199
202
  answer_options: [
200
203
  {
201
- id: 1,
204
+ id: firstAnswerId,
202
205
  value: '',
203
206
  correctAnswer: true,
204
207
  disabled: false,
208
+ chosen: false,
209
+ focus: false,
205
210
  },
206
211
  {
207
- id: 2,
212
+ id: Crypto.id(),
208
213
  value: '',
209
214
  correctAnswer: false,
210
215
  disabled: false,
216
+ chosen: false,
217
+ focus: false,
211
218
  },
212
219
  ],
220
+ correctAnswer: firstAnswerId,
213
221
  body: '',
214
222
  hint: '',
215
223
  answer_description: '',
224
+ id: Crypto.id(),
216
225
  })
217
226
  this.editingIndex =
218
227
  this.block.metadata.config.questions.length - 1
@@ -222,21 +231,26 @@ export default {
222
231
  this.dialog = true
223
232
  },
224
233
  saveAndNewCalled() {
234
+ let firstAnswerId = Crypto.id()
225
235
  this.block.metadata.config.questions.push({
226
236
  answer_options: [
227
237
  {
228
- id: 1,
238
+ id: firstAnswerId,
229
239
  value: '',
230
240
  correctAnswer: true,
231
241
  disabled: false,
242
+ chosen: false,
232
243
  },
233
244
  {
234
- id: 2,
245
+ id: Crypto.id(),
235
246
  value: '',
236
247
  correctAnswer: false,
237
248
  disabled: false,
249
+ chosen: false,
238
250
  },
239
251
  ],
252
+ // needed to add this bc radio group needs the id of the answer in answerobjects array to set correct value
253
+ correctAnswer: firstAnswerId,
240
254
  body: '',
241
255
  hint: '',
242
256
  answer_description: '',
@@ -0,0 +1,288 @@
1
+ <template>
2
+ <div>
3
+ <v-text-field
4
+ v-model="block.metadata.config.title"
5
+ :counter="50"
6
+ :label="
7
+ $t('plugin.games.components.settings.bucket_game.form.title')
8
+ "
9
+ ref="title"
10
+ ></v-text-field>
11
+ <br />
12
+ <v-textarea
13
+ outlined
14
+ auto-grow
15
+ v-model="block.metadata.config.instructions"
16
+ :label="
17
+ $t(
18
+ 'plugin.games.components.settings.bucket_game.form.instructions'
19
+ )
20
+ "
21
+ ></v-textarea>
22
+ <v-expansion-panels flat>
23
+ <v-expansion-panel>
24
+ <v-expansion-panel-header class="pa-0">
25
+ <h3>
26
+ {{
27
+ $t(
28
+ 'plugin.games.components.settings.seven_strikes.items'
29
+ )
30
+ }}
31
+ </h3>
32
+ </v-expansion-panel-header>
33
+ <v-expansion-panel-content class="ma-0">
34
+ <v-card
35
+ v-for="(item, index) in block.metadata.config.words"
36
+ :key="'bucket_' + index"
37
+ class="pa-2 flex-fill cardOutline"
38
+ elevation="0"
39
+ outlined
40
+ tile
41
+ >
42
+ <v-row class="itemHeight">
43
+ <p
44
+ class="text-truncate pa-0 ma-0 seventy"
45
+ @click="onOpenEditor(index)"
46
+ v-on:keyup.enter="onOpenEditor(index)"
47
+ @mouseover="onHover"
48
+ @mouseleave="onHoverLeave"
49
+ :class="cursor"
50
+ tabindex="0"
51
+ >
52
+ <v-icon class="pl-2"
53
+ >mdi-gesture-tap-hold</v-icon
54
+ >
55
+ {{
56
+ item.value
57
+ ? block.metadata.config.words[index]
58
+ .value
59
+ : $t(
60
+ 'plugin.games.components.settings.bucket_game.form.enter_text'
61
+ )
62
+ }}
63
+ </p>
64
+ <v-icon class="twenty" @click="onDelete(index)"
65
+ >mdi-delete-outline</v-icon
66
+ >
67
+ </v-row>
68
+ <v-textarea
69
+ v-if="item.expand"
70
+ outlined
71
+ :autofocus="true"
72
+ :label="
73
+ $t(
74
+ 'plugin.games.components.settings.seven_strikes.word'
75
+ )
76
+ "
77
+ class="pt-4"
78
+ v-model="block.metadata.config.words[index].value"
79
+ ></v-textarea>
80
+ <v-textarea
81
+ v-if="item.expand"
82
+ outlined
83
+ class="pt-4"
84
+ :label="
85
+ $t(
86
+ 'plugin.games.components.settings.seven_strikes.hint'
87
+ )
88
+ "
89
+ v-model="block.metadata.config.words[index].hint"
90
+ ></v-textarea>
91
+ </v-card>
92
+ <p
93
+ @mouseover="onHover"
94
+ @mouseleave="onHoverLeave"
95
+ @click="onAddElement"
96
+ v-on:keyup.enter="onAddElement"
97
+ class="fullWidth"
98
+ :class="cursor"
99
+ tabindex="0"
100
+ >
101
+ <v-icon class="primary addIcon">mdi-plus</v-icon>
102
+ {{
103
+ $t(
104
+ 'plugin.games.components.content.blocks.sorting_game.add_element'
105
+ )
106
+ }}
107
+ </p>
108
+ </v-expansion-panel-content>
109
+ </v-expansion-panel>
110
+ </v-expansion-panels>
111
+ <v-row class="mt-3">
112
+ <v-textarea
113
+ v-model="block.metadata.config.feedback_correct"
114
+ outlined
115
+ auto-grow
116
+ :counter="50"
117
+ :label="
118
+ $t(
119
+ 'plugin.games.components.settings.word_jumble.feedback_correct'
120
+ )
121
+ "
122
+ ref="title"
123
+ ></v-textarea>
124
+ <br />
125
+ <v-textarea
126
+ outlined
127
+ auto-grow
128
+ v-model="block.metadata.config.feedback_incorrect"
129
+ :label="
130
+ $t(
131
+ 'plugin.games.components.settings.word_jumble.feedback_incorrect'
132
+ )
133
+ "
134
+ ></v-textarea>
135
+ <br />
136
+ </v-row>
137
+ </div>
138
+ </template>
139
+
140
+ <script>
141
+ import _ from 'lodash'
142
+ import draggable from 'vuedraggable'
143
+ import BaseContentSettings from '~/components/Content/Tool/BaseContentSettings.js'
144
+ import CrudTable from '../content/CrudTable.vue'
145
+
146
+ export default {
147
+ name: 'SevenStrikesSettingsManager',
148
+ extends: BaseContentSettings,
149
+ components: { CrudTable, draggable },
150
+ beforeMount() {
151
+ if (_.isEmpty(this.block)) {
152
+ this.block = {}
153
+ }
154
+ if (_.isEmpty(this.block.metadata)) {
155
+ this.block.metadata = {}
156
+ }
157
+ if (_.isEmpty(this.block.metadata.config)) {
158
+ this.block.metadata.config = {}
159
+ }
160
+ if (_.isEmpty(this.block.metadata.config.title)) {
161
+ this.block.metadata.config.title = ''
162
+ }
163
+ if (_.isEmpty(this.block.metadata.config.instructions)) {
164
+ this.block.metadata.config.instructions = this.$t(
165
+ 'plugin.games.components.settings.seven_strikes.instructions'
166
+ )
167
+ }
168
+ if (_.isEmpty(this.block.metadata.config.feedback_correct)) {
169
+ this.block.metadata.config.feedback_correct = ''
170
+ }
171
+ if (_.isEmpty(this.block.metadata.config.feedback_correct)) {
172
+ this.block.metadata.config.feedback_incorrect = ''
173
+ }
174
+ if (_.isEmpty(this.block.metadata.config.words)) {
175
+ this.block.metadata.config.words = []
176
+ const default_object = {
177
+ value: '',
178
+ expand: true,
179
+ hint: '',
180
+ splitWord: '',
181
+ }
182
+ this.block.metadata.config.words.push(default_object)
183
+ }
184
+ },
185
+ data() {
186
+ return {
187
+ cursor: null,
188
+ }
189
+ },
190
+ methods: {
191
+ onBeforeSave() {
192
+ this.block.metadata.config.words.forEach((element) => {
193
+ // check against lowercase keyboard inputs
194
+ element.value = element.value.toLowerCase()
195
+ element.splitWord = element.value.split('')
196
+ element.splitWord.forEach((letter) => {
197
+ const letterIndex = element.splitWord.indexOf(letter)
198
+ element.splitWord[letterIndex] = {
199
+ letter: letter,
200
+ show: false,
201
+ }
202
+ })
203
+ })
204
+ },
205
+ onAddElement() {
206
+ this.block.metadata.config.words.forEach((element) => {
207
+ element.expand = false
208
+ })
209
+ const default_object = {
210
+ value: '',
211
+ expand: true,
212
+ hint: '',
213
+ splitWord: '',
214
+ }
215
+ this.block.metadata.config.words.push(default_object)
216
+ },
217
+ onHover(index) {
218
+ this.cursor = 'changePointer'
219
+ },
220
+ onHoverLeave() {
221
+ this.cursor = ''
222
+ },
223
+ onOpenEditor(index) {
224
+ // open target element
225
+ this.block.metadata.config.words[index].expand =
226
+ !this.block.metadata.config.words[index].expand
227
+ // close all other expanded areas
228
+ this.block.metadata.config.words.forEach((element) => {
229
+ let indexOf = this.block.metadata.config.words.indexOf(element)
230
+ if (index !== indexOf) {
231
+ element.expand = false
232
+ }
233
+ })
234
+ },
235
+ onDelete(index) {
236
+ this.block.metadata.config.words.splice(index, 1)
237
+ },
238
+ },
239
+ }
240
+ </script>
241
+
242
+ <style lang="scss" scoped>
243
+ .v-progress-circular {
244
+ margin: 1rem;
245
+ }
246
+ .cardOutline {
247
+ border: 2px solid var(--v-primary-base);
248
+ margin-bottom: 8px;
249
+ }
250
+ .fullWidth {
251
+ display: flex;
252
+ justify-content: center;
253
+ align-items: center;
254
+ font-weight: bold;
255
+ font-size: 16px;
256
+ }
257
+ .itemHeight {
258
+ display: flex;
259
+ align-content: center;
260
+ justify-content: space-between;
261
+ height: 60px;
262
+ }
263
+ .changePointer {
264
+ cursor: pointer !important;
265
+ }
266
+ .seventy {
267
+ width: 70%;
268
+ height: 100%;
269
+ display: flex;
270
+ align-items: center;
271
+ }
272
+ .twenty {
273
+ width: 20%;
274
+ height: 90%;
275
+ margin-top: 3px;
276
+ border-left: 2px solid var(--v-primary-base);
277
+ }
278
+ .addButton {
279
+ height: 50%;
280
+ width: 50%;
281
+ }
282
+ .addIcon {
283
+ margin-right: 5px;
284
+ }
285
+ .v-expansion-panel-content::v-deep .v-expansion-panel-content__wrap {
286
+ padding: 0 !important;
287
+ }
288
+ </style>
@@ -209,11 +209,14 @@ export default {
209
209
  },
210
210
  methods: {
211
211
  onAddWord() {
212
+ this.block.metadata.config.words.forEach((element) => {
213
+ element.expand = false
214
+ })
212
215
  let defaultWord = {
213
216
  id: this.block.metadata.config.words.length + 1,
214
217
  value: '',
215
218
  hint: '',
216
- expand: false,
219
+ expand: true,
217
220
  }
218
221
  this.block.metadata.config.words.push(defaultWord)
219
222
  },
@@ -0,0 +1,22 @@
1
+ export default {
2
+ crossword: 'Crossword Game',
3
+ add_element: 'Add Element',
4
+ initial_setup:
5
+ 'Enter at least two words with clues to generate a crossword',
6
+ validation_error:
7
+ 'The crossword could not be loaded because of the below error.',
8
+ error: {
9
+ min_words: 'You must enter at least two words with clues!',
10
+ words_no_shared_letters:
11
+ 'The following words do not share letters: {0}',
12
+ word_length_max_limit: 'Cannot use words over 20 characters long.',
13
+ word_length_min_limit: 'Cannot use words under 2 characters long.',
14
+ could_not_generate:
15
+ 'Could not generate a {0} by {0} crossword with the supplied words',
16
+ unknown: 'An unknown error occurred during the crossword creation',
17
+ },
18
+
19
+ play_again: 'Randomize Crossword and Play Again',
20
+ across: 'Across',
21
+ down: 'Down',
22
+ }