Merge 6cfaf63502 into 63fbd3b45c
				
					
				
			This commit is contained in:
		
						commit
						b62538b1b6
					
				| @ -72,6 +72,7 @@ | ||||
|     "js-base64": "^3.7.6", | ||||
|     "json5": "^2.2.3", | ||||
|     "jwt-decode": "^3.1.2", | ||||
|     "korean-unpacker": "^1.0.3", | ||||
|     "libphonenumber-js": "^1.10.28", | ||||
|     "lodash": "^4.17.21", | ||||
|     "markdown-it": "^14.0.0", | ||||
|  | ||||
							
								
								
									
										13
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -113,6 +113,9 @@ dependencies: | ||||
|   jwt-decode: | ||||
|     specifier: ^3.1.2 | ||||
|     version: 3.1.2 | ||||
|   korean-unpacker: | ||||
|     specifier: ^1.0.3 | ||||
|     version: 1.0.3 | ||||
|   libphonenumber-js: | ||||
|     specifier: ^1.10.28 | ||||
|     version: 1.10.28 | ||||
| @ -3412,7 +3415,7 @@ packages: | ||||
|     dependencies: | ||||
|       '@unhead/dom': 0.5.1 | ||||
|       '@unhead/schema': 0.5.1 | ||||
|       '@vueuse/shared': 11.0.3(vue@3.3.4) | ||||
|       '@vueuse/shared': 11.1.0(vue@3.3.4) | ||||
|       unhead: 0.5.1 | ||||
|       vue: 3.3.4 | ||||
|     transitivePeerDependencies: | ||||
| @ -4054,8 +4057,8 @@ packages: | ||||
|       - vue | ||||
|     dev: false | ||||
| 
 | ||||
|   /@vueuse/shared@11.0.3(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-0rY2m6HS5t27n/Vp5cTDsKTlNnimCqsbh/fmT2LgE+aaU42EMfXo8+bNX91W9I7DDmxfuACXMmrd7d79JxkqWA==} | ||||
|   /@vueuse/shared@11.1.0(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-YUtIpY122q7osj+zsNMFAfMTubGz0sn5QzE5gPzAIiCmtt2ha3uQUY1+JPyL4gRCTsLPX82Y9brNbo/aqlA91w==} | ||||
|     dependencies: | ||||
|       vue-demi: 0.14.10(vue@3.3.4) | ||||
|     transitivePeerDependencies: | ||||
| @ -6699,6 +6702,10 @@ packages: | ||||
|     resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /korean-unpacker@1.0.3: | ||||
|     resolution: {integrity: sha512-s0VIyZOFOtalBSp51NECPvM1g9Af8M0Oxe1ZdMMGL7nvG/ecVNYkCAzPU+f52f0R83ytVCOd3fd14TTOFx2GXQ==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /less@4.1.3: | ||||
|     resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==} | ||||
|     engines: {node: '>=6'} | ||||
|  | ||||
							
								
								
									
										3
									
								
								src/tools/text-to-nato-alphabet/korean-unpacker.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/tools/text-to-nato-alphabet/korean-unpacker.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| declare module 'korean-unpacker' { | ||||
|     function unpack(text: string): string; | ||||
| } | ||||
							
								
								
									
										2844
									
								
								src/tools/text-to-nato-alphabet/nato.alphabets.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2844
									
								
								src/tools/text-to-nato-alphabet/nato.alphabets.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										137
									
								
								src/tools/text-to-nato-alphabet/nato.test.data.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/tools/text-to-nato-alphabet/nato.test.data.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,137 @@ | ||||
| [ | ||||
| { | ||||
|     "lang": "(International)", | ||||
|     "input": "TesYW 123 ? = !@", | ||||
|     "output": "TANGO echo sierra YANKEE WHISKEY (SPACE) ONE TWO THREE (SPACE) (?) (SPACE) (=) (SPACE) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(France)", | ||||
|     "input": "TesYW 123 ? = !@", | ||||
|     "output": "THÉRÈSE eugène suzanne YVONNE WILLIAM (ESPACE) UN DEUX TROIS (ESPACE) (?) (ESPACE) (=) (ESPACE) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(Belgium)", | ||||
|     "input": "TesYW 123 ? = !@", | ||||
|     "output": "TELEFOON emiel sofie YVONNE WATERLOO (ESPACE) (1) (2) (3) (ESPACE) (?) (ESPACE) (=) (ESPACE) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(Switzerland)", | ||||
|     "input": "TesYW 123 ? = !@", | ||||
|     "output": "THÉRÈSE émile suzanne (Y) WILLIAM (ESPACE) (1) (2) (3) (ESPACE) (?) (ESPACE) (=) (ESPACE) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(Québec)", | ||||
|     "input": "TesYW 123 ? = !@", | ||||
|     "output": "THOMAS édouard samuel (Y) WILLIAM (ESPACE) (1) (2) (3) (ESPACE) (?) (ESPACE) (=) (ESPACE) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(Germany, 2022)", | ||||
|     "input": "TesYü 123 ? = !@", | ||||
|     "output": "TÜBINGEN essen salzwedel YPSILON umlaut-unna (LEERZEICHEN) (1) (2) (3) (LEERZEICHEN) (?) (LEERZEICHEN) (=) (LEERZEICHEN) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(Austria)", | ||||
|     "input": "TesYW 123 ? = !@", | ||||
|     "output": "THEODOR emil samuel/siegfried YPSILON WILHELM (LEERZEICHEN) (1) (2) (3) (LEERZEICHEN) (?) (LEERZEICHEN) (=) (LEERZEICHEN) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(Germany, informal, 2022)", | ||||
|     "input": "TesYü 123 ? = !@", | ||||
|     "output": "THEODOR emil samuel YPSILON überfluss (LEERZEICHEN) (1) (2) (3) (LEERZEICHEN) (?) (LEERZEICHEN) (=) (LEERZEICHEN) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(Netherlands)", | ||||
|     "input": "TesY huis rij 123 ? = !@", | ||||
|     "output": "THEODOR eduard simon YPSILON (SPATIE) hendrik utrecht izaak simon (SPATIE) richard/rudolf ijmuiden/ijsbrand (SPATIE) (1) (2) (3) (SPATIE) (?) (SPATIE) (=) (SPATIE) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Italian", | ||||
|     "input": "TesYj 123 ? = !@", | ||||
|     "output": "TORINO empoli savona YORK, YOGURT jolly/juventus (SPAZIO) (1) (2) (3) (SPAZIO) (?) (SPAZIO) (=) (SPAZIO) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Spanish", | ||||
|     "input": "TesYñj 123 ? = !@", | ||||
|     "output": "TOLEDO españa sábado YOLANDA ñoño josé (ESPACIO) (1) (2) (3) (ESPACIO) (?) (ESPACIO) (=) (ESPACIO) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(Brazil)", | ||||
|     "input": "TesYÇj 123 ? = !@", | ||||
|     "output": "TATU estrela saci YOLANDA (Ç) josé (ESPAÇO) (1) (2) (3) (ESPAÇO) (?) (ESPAÇO) (=) (ESPAÇO) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "(Portugal)", | ||||
|     "input": "TesYúj 123 ? = !@", | ||||
|     "output": "TAVIRA évora setúbal YORK (ú) josé (ESPAÇO) (1) (2) (3) (ESPAÇO) (?) (ESPAÇO) (=) (ESPAÇO) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Swedish", | ||||
|     "input": "TesYj 123 ? = !@", | ||||
|     "output": "TORE erik sigurd YNGVE johan (UTRYMME) (1) (2) (3) (UTRYMME) (?) (UTRYMME) (=) (UTRYMME) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Danish", | ||||
|     "input": "TesÆYj 123 ? = !@", | ||||
|     "output": "THEODOR erik søren ÆGIR YRSA johan (MELLEMRUMSTEGN) (1) (2) (3) (MELLEMRUMSTEGN) (?) (MELLEMRUMSTEGN) (=) (MELLEMRUMSTEGN) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Norwegian", | ||||
|     "input": "TesYÅj 123 ? = !@", | ||||
|     "output": "TEODOR edith sigrid YNGLING ÅSE johan (MELLOMROMSTEGN) (1) (2) (3) (MELLOMROMSTEGN) (?) (MELLOMROMSTEGN) (=) (MELLOMROMSTEGN) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Finnish", | ||||
|     "input": "TesYj 123 ? = !@", | ||||
|     "output": "TYYNE eemeli sakari YRJÖ jussi (VÄLILYÖNTI) (1) (2) (3) (VÄLILYÖNTI) (?) (VÄLILYÖNTI) (=) (VÄLILYÖNTI) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Turkish", | ||||
|     "input": "TesYj 123 ? = !@", | ||||
|     "output": "TOKAT edirne sinop YOZGAT jandarma (BOŞLUK) (1) (2) (3) (BOŞLUK) (?) (BOŞLUK) (=) (BOŞLUK) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Romanian", | ||||
|     "input": "TesYj 123 ? = !@", | ||||
|     "output": "TUDOR elena sandu I-GREC jean (SPAȚIU) (1) (2) (3) (SPAȚIU) (?) (SPAȚIU) (=) (SPAȚIU) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Czech", | ||||
|     "input": "TesYj 123 ? = !@", | ||||
|     "output": "TOMÁŠ emil svatopluk YPSILON josef (PROSTOROVÝ) (1) (2) (3) (PROSTOROVÝ) (?) (PROSTOROVÝ) (=) (PROSTOROVÝ) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Yugoslav", | ||||
|     "input": "TesYČj 123 ? = !@", | ||||
|     "output": "TUZLA evropa skopje IPSILON ČAČAK jadran (PRIESTOROVÝ) (1) (2) (3) (PRIESTOROVÝ) (?) (PRIESTOROVÝ) (=) (PRIESTOROVÝ) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Serbian", | ||||
|     "input": "TesYČj 123 ? = !@", | ||||
|     "output": "TIMOK evropa sava IPSILON ČAČAK jadran (PROSTORNI) (1) (2) (3) (PROSTORNI) (?) (PROSTORNI) (=) (PROSTORNI) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Slovene", | ||||
|     "input": "TesYČj 123 ? = !@", | ||||
|     "output": "TRIGLAV evropa soča IPSILON ČATEŽ jadran (PRESLEDKA) (1) (2) (3) (PRESLEDKA) (?) (PRESLEDKA) (=) (PRESLEDKA) (!) (@)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Russian", | ||||
|     "input": "Зинаида !.?", | ||||
|     "output": "ЗИНАИДА иван николай анна иван дмитрий анна ( ) (!) (.) (?)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Korean", | ||||
|     "input": "안녕하세요 여러분", | ||||
|     "output": "잉어 아버지 나폴리 나폴리 연못 잉어 한강 아버지 서울 엑스레이 잉어 요지경 ( ) 잉어 연못 로마 어머니 바가지 우편 나폴리" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Greek", | ||||
|     "input": "τίγρης !?", | ||||
|     "output": "τίγρης (ί) γαλή ρήγας ηρώ σοφός ( ) (!) (?)" | ||||
|  }, | ||||
|  { | ||||
|     "lang": "Japanese", | ||||
|     "input": "数字のひと おしまいのン ?:", | ||||
|     "output": "(数) (字) 野原のノ 飛行機のヒ 東京のト ( ) 大阪のオ 新聞のシ マッチのマ いろはのイ 野原のノ おしまいのン ( ) (?) (:)" | ||||
|  } | ||||
| ] | ||||
| @ -1,28 +1,29 @@ | ||||
| export const natoAlphabet = [ | ||||
|   'Alpha', | ||||
|   'Bravo', | ||||
|   'Charlie', | ||||
|   'Delta', | ||||
|   'Echo', | ||||
|   'Foxtrot', | ||||
|   'Golf', | ||||
|   'Hotel', | ||||
|   'India', | ||||
|   'Juliet', | ||||
|   'Kilo', | ||||
|   'Lima', | ||||
|   'Mike', | ||||
|   'November', | ||||
|   'Oscar', | ||||
|   'Papa', | ||||
|   'Quebec', | ||||
|   'Romeo', | ||||
|   'Sierra', | ||||
|   'Tango', | ||||
|   'Uniform', | ||||
|   'Victor', | ||||
|   'Whiskey', | ||||
|   'X-ray', | ||||
|   'Yankee', | ||||
|   'Zulu', | ||||
| export const allLanguagesAndCountries = [ | ||||
|   '(International)', | ||||
|   '(France)', | ||||
|   '(Belgium)', | ||||
|   '(Switzerland)', | ||||
|   '(Québec)', | ||||
|   '(Germany, 2022)', | ||||
|   '(Austria)', | ||||
|   '(Germany, informal, 2022)', | ||||
|   '(Netherlands)', | ||||
|   'Italian', | ||||
|   'Spanish', | ||||
|   '(Brazil)', | ||||
|   '(Portugal)', | ||||
|   'Swedish', | ||||
|   'Danish', | ||||
|   'Norwegian', | ||||
|   'Finnish', | ||||
|   'Turkish', | ||||
|   'Romanian', | ||||
|   'Czech', | ||||
|   'Yugoslav', | ||||
|   'Serbian', | ||||
|   'Slovene', | ||||
|   'Russian', | ||||
|   'Korean', | ||||
|   'Greek', | ||||
|   'Japanese', | ||||
| ]; | ||||
|  | ||||
| @ -0,0 +1,160 @@ | ||||
| import { describe, expect, it } from 'vitest'; | ||||
| import { textToNatoAlphabet } from './text-to-nato-alphabet.service'; | ||||
| 
 | ||||
| const TestCase_TestYW = 'TesYW 123 ? = !@'; | ||||
| const TestCase_TestYJ = 'TesYj 123 ? = !@'; | ||||
| const natoTests = [ | ||||
|   { | ||||
|     lang: '(International)', | ||||
|     input: TestCase_TestYW, | ||||
|     output: 'TANGO echo sierra YANKEE WHISKEY (SPACE) (digit 1) (digit 2) (digit 3) (SPACE) (punctuation ?) (SPACE) (punctuation =) (SPACE) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(France)', | ||||
|     input: TestCase_TestYW, | ||||
|     output: 'THÉRÈSE eugène suzanne YVONNE WILLIAM (ESPACE) (digit 1) (digit 2) (digit 3) (ESPACE) (punctuation ?) (ESPACE) (punctuation =) (ESPACE) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(Belgium)', | ||||
|     input: TestCase_TestYW, | ||||
|     output: 'TELEFOON emiel sofie YVONNE WATERLOO (ESPACE) (digit 1) (digit 2) (digit 3) (ESPACE) (punctuation ?) (ESPACE) (punctuation =) (ESPACE) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(Switzerland)', | ||||
|     input: TestCase_TestYW, | ||||
|     output: 'THÉRÈSE émile suzanne (Y) WILLIAM (ESPACE) (digit 1) (digit 2) (digit 3) (ESPACE) (punctuation ?) (ESPACE) (punctuation =) (ESPACE) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(Québec)', | ||||
|     input: TestCase_TestYW, | ||||
|     output: 'THOMAS édouard samuel (Y) WILLIAM (ESPACE) (digit 1) (digit 2) (digit 3) (ESPACE) (punctuation ?) (ESPACE) (punctuation =) (ESPACE) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(Germany, 2022)', | ||||
|     input: 'TesYü 123 ? = !@', | ||||
|     output: 'TÜBINGEN essen salzwedel YPSILON umlaut-unna (LEERZEICHEN) (digit 1) (digit 2) (digit 3) (LEERZEICHEN) (punctuation ?) (LEERZEICHEN) (punctuation =) (LEERZEICHEN) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(Austria)', | ||||
|     input: TestCase_TestYW, | ||||
|     output: 'THEODOR emil samuel/siegfried YPSILON WILHELM (LEERZEICHEN) (digit 1) (digit 2) (digit 3) (LEERZEICHEN) (punctuation ?) (LEERZEICHEN) (punctuation =) (LEERZEICHEN) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(Germany, informal, 2022)', | ||||
|     input: 'TesYü 123 ? = !@', | ||||
|     output: 'THEODOR emil samuel YPSILON überfluss (LEERZEICHEN) (digit 1) (digit 2) (digit 3) (LEERZEICHEN) (punctuation ?) (LEERZEICHEN) (punctuation =) (LEERZEICHEN) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(Netherlands)', | ||||
|     input: 'TesY huis rij 123 ? = !@', | ||||
|     output: 'THEODOR eduard simon YPSILON (SPATIE) hendrik utrecht izaak simon (SPATIE) richard/rudolf ijmuiden/ijsbrand (SPATIE) (digit 1) (digit 2) (digit 3) (SPATIE) (punctuation ?) (SPATIE) (punctuation =) (SPATIE) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Italian', | ||||
|     input: TestCase_TestYJ, | ||||
|     output: 'TORINO empoli savona YORK, YOGURT jolly/juventus (SPAZIO) (digit 1) (digit 2) (digit 3) (SPAZIO) (punctuation ?) (SPAZIO) (punctuation =) (SPAZIO) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Spanish', | ||||
|     input: 'TesYñj 123 ? = !@', | ||||
|     output: 'TOLEDO españa sábado YOLANDA ñoño josé (ESPACIO) (digit 1) (digit 2) (digit 3) (ESPACIO) (punctuation ?) (ESPACIO) (punctuation =) (ESPACIO) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(Brazil)', | ||||
|     input: 'TesYÇj 123 ? = !@', | ||||
|     output: 'TATU estrela saci YOLANDA (Ç) josé (ESPAÇO) (digit 1) (digit 2) (digit 3) (ESPAÇO) (punctuation ?) (ESPAÇO) (punctuation =) (ESPAÇO) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: '(Portugal)', | ||||
|     input: 'TesYúj 123 ? = !@', | ||||
|     output: 'TAVIRA évora setúbal YORK (ú) josé (ESPAÇO) (digit 1) (digit 2) (digit 3) (ESPAÇO) (punctuation ?) (ESPAÇO) (punctuation =) (ESPAÇO) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Swedish', | ||||
|     input: TestCase_TestYJ, | ||||
|     output: 'TORE erik sigurd YNGVE johan (UTRYMME) (digit 1) (digit 2) (digit 3) (UTRYMME) (punctuation ?) (UTRYMME) (punctuation =) (UTRYMME) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Danish', | ||||
|     input: 'TesÆYj 123 ? = !@', | ||||
|     output: 'THEODOR erik søren ÆGIR YRSA johan (MELLEMRUMSTEGN) (digit 1) (digit 2) (digit 3) (MELLEMRUMSTEGN) (punctuation ?) (MELLEMRUMSTEGN) (punctuation =) (MELLEMRUMSTEGN) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Norwegian', | ||||
|     input: 'TesYÅj 123 ? = !@', | ||||
|     output: 'TEODOR edith sigrid YNGLING ÅSE johan (MELLOMROMSTEGN) (digit 1) (digit 2) (digit 3) (MELLOMROMSTEGN) (punctuation ?) (MELLOMROMSTEGN) (punctuation =) (MELLOMROMSTEGN) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Finnish', | ||||
|     input: TestCase_TestYJ, | ||||
|     output: 'TYYNE eemeli sakari YRJÖ jussi (VÄLILYÖNTI) (digit 1) (digit 2) (digit 3) (VÄLILYÖNTI) (punctuation ?) (VÄLILYÖNTI) (punctuation =) (VÄLILYÖNTI) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Turkish', | ||||
|     input: TestCase_TestYJ, | ||||
|     output: 'TOKAT edirne sinop YOZGAT jandarma (BOŞLUK) (digit 1) (digit 2) (digit 3) (BOŞLUK) (punctuation ?) (BOŞLUK) (punctuation =) (BOŞLUK) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Romanian', | ||||
|     input: TestCase_TestYJ, | ||||
|     output: 'TUDOR elena sandu I-GREC jean (SPAȚIU) (digit 1) (digit 2) (digit 3) (SPAȚIU) (punctuation ?) (SPAȚIU) (punctuation =) (SPAȚIU) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Czech', | ||||
|     input: TestCase_TestYJ, | ||||
|     output: 'TOMÁŠ emil svatopluk YPSILON josef (PROSTOROVÝ) (digit 1) (digit 2) (digit 3) (PROSTOROVÝ) (punctuation ?) (PROSTOROVÝ) (punctuation =) (PROSTOROVÝ) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Yugoslav', | ||||
|     input: 'TesYČj 123 ? = !@', | ||||
|     output: 'TUZLA evropa skopje IPSILON ČAČAK jadran (PRIESTOROVÝ) (digit 1) (digit 2) (digit 3) (PRIESTOROVÝ) (punctuation ?) (PRIESTOROVÝ) (punctuation =) (PRIESTOROVÝ) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Serbian', | ||||
|     input: 'TesYČj 123 ? = !@', | ||||
|     output: 'TIMOK evropa sava IPSILON ČAČAK jadran (PROSTORNI) (digit 1) (digit 2) (digit 3) (PROSTORNI) (punctuation ?) (PROSTORNI) (punctuation =) (PROSTORNI) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Slovene', | ||||
|     input: 'TesYČj 123 ? = !@', | ||||
|     output: 'TRIGLAV evropa soča IPSILON ČATEŽ jadran (PRESLEDKA) (digit 1) (digit 2) (digit 3) (PRESLEDKA) (punctuation ?) (PRESLEDKA) (punctuation =) (PRESLEDKA) (punctuation !) (punctuation @)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Russian', | ||||
|     input: 'Зинаида !.?', | ||||
|     output: 'ЗИНАИДА иван николай анна иван дмитрий анна ( ) (punctuation !) (punctuation .) (punctuation ?)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Korean', | ||||
|     input: '안녕하세요 여러분', | ||||
|     output: '잉어 아버지 나폴리 나폴리 연못 잉어 한강 아버지 서울 엑스레이 잉어 요지경 ( ) 잉어 연못 로마 어머니 바가지 우편 나폴리', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Greek', | ||||
|     input: 'τίγρης !?', | ||||
|     output: 'τίγρης (ί) γαλή ρήγας ηρώ σοφός ( ) (punctuation !) (punctuation ?)', | ||||
|   }, | ||||
|   { | ||||
|     lang: 'Japanese', | ||||
|     input: '数字のひと おしまいのン ?:', | ||||
|     output: '(数) (字) 野原のノ 飛行機のヒ 東京のト ( ) 大阪のオ 新聞のシ マッチのマ いろはのイ 野原のノ おしまいのン ( ) (punctuation ?) (punctuation :)', | ||||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| describe('text-to-nato', () => { | ||||
|   it('Convert text to NATO', async () => { | ||||
|     for (const nato of natoTests) { | ||||
|       const { lang, input, output } = nato; | ||||
|       expect(textToNatoAlphabet({ text: input, langOrCountry: lang })).to.equal(output); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   it('Convert text to NATO (includes punctuations and digits names)', async () => { | ||||
|     expect(textToNatoAlphabet({ text: 'a 1 2 3 ! % ? ;', langOrCountry: '(France)', useDigitsNames: true, usePunctuationsNames: true })).to.equal( | ||||
|       'anatole (ESPACE) {digit 1 => un} (ESPACE) {digit 2 => deux} (ESPACE) {digit 3 => trois} (ESPACE) {punctuation ! => point d\'exclamation} (ESPACE) {punctuation % => pour cent} (ESPACE) {punctuation ? => point d\'interrogation} (ESPACE) {punctuation ; => point-virgule}'); | ||||
|     expect(textToNatoAlphabet({ text: 'и 1 2 3 ! % ? ;', langOrCountry: 'Russian', useDigitsNames: true, usePunctuationsNames: true })).to.equal( | ||||
|       'иван ( ) {digit 1 => один (odin)} ( ) {digit 2 => два (dva)} ( ) {digit 3 => три (tri)} ( ) {punctuation ! => восклицательный знак (vosklitsatel\'nyy znak)} ( ) {punctuation % => процент (protsent)} ( ) {punctuation ? => вопросительный знак (voprositel\'nyy znak)} ( ) {punctuation ; => точка с запятой (tochka s zapyatoy)}'); | ||||
|     expect(textToNatoAlphabet({ text: 'TesYj 1 2 3 ! % ? ;', langOrCountry: 'Swedish', useDigitsNames: true, usePunctuationsNames: true })).to.equal( | ||||
|       'TORE erik sigurd YNGVE johan (UTRYMME) {digit 1 => ett} (UTRYMME) {digit 2 => två} (UTRYMME) {digit 3 => tre} (UTRYMME) {punctuation ! => utropstecken} (UTRYMME) {punctuation % => procent} (UTRYMME) {punctuation ? => frågetecken} (UTRYMME) {punctuation ; => semikolon}'); | ||||
|   }); | ||||
| }); | ||||
| @ -1,19 +1,80 @@ | ||||
| import { natoAlphabet } from './text-to-nato-alphabet.constants'; | ||||
| import hangul from 'korean-unpacker'; | ||||
| import allAlphabets from './nato.alphabets.json'; | ||||
| 
 | ||||
| type AllAlphabetsKeys = keyof typeof allAlphabets[0]; | ||||
| 
 | ||||
| export { textToNatoAlphabet }; | ||||
| 
 | ||||
| function getLetterPositionInAlphabet({ letter }: { letter: string }) { | ||||
|   return letter.toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0); | ||||
| function isPunctuation(char: string) { | ||||
|   const punctuations = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'; | ||||
|   return punctuations.includes(char); | ||||
| } | ||||
| function isDigit(char: string) { | ||||
|   const digits = '0123456789'; | ||||
|   return digits.includes(char); | ||||
| } | ||||
| 
 | ||||
| function textToNatoAlphabet({ text }: { text: string }) { | ||||
|   return text | ||||
|     .split('') | ||||
|     .map((character) => { | ||||
|       const alphabetIndex = getLetterPositionInAlphabet({ letter: character }); | ||||
|       const natoWord = natoAlphabet[alphabetIndex]; | ||||
| function escapeRegExp(string: string) { | ||||
|   return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
 | ||||
| } | ||||
| 
 | ||||
|       return natoWord ?? character; | ||||
| function textToNatoAlphabet({ | ||||
|   text, langOrCountry = '(International)', | ||||
|   useDigitsNames = false, usePunctuationsNames = false, | ||||
| }: { | ||||
|   text: string | ||||
|   langOrCountry: string | ||||
|   useDigitsNames?: boolean | ||||
|   usePunctuationsNames?: boolean | ||||
| }) { | ||||
|   const getNatoWord = (searchChar: string) => { | ||||
|     const alphabetLetter = allAlphabets.find(letter => letter.Letter === searchChar); | ||||
|     if (alphabetLetter && alphabetLetter[langOrCountry as AllAlphabetsKeys]) { | ||||
|       return alphabetLetter[langOrCountry as AllAlphabetsKeys] || ''; | ||||
|     } | ||||
|     return null; | ||||
|   }; | ||||
| 
 | ||||
|   const charRegex = new RegExp( | ||||
|     `(${ | ||||
|         allAlphabets | ||||
|         .sort((a, b) => b.Letter.length - a.Letter.length) | ||||
|         .filter(a => a[langOrCountry as AllAlphabetsKeys]) | ||||
|         .map(a => escapeRegExp(a.Letter)) | ||||
|         .join('|') | ||||
|         }|.)`,
 | ||||
|     'gi'); | ||||
|   return hangul.unpack(text) | ||||
|     .replace(/\s+/g, ' ') | ||||
|     .replace( | ||||
|       charRegex, | ||||
|       (character) => { | ||||
|         const searchChar = character.toUpperCase(); | ||||
|         const isUpper = character[0].toUpperCase() === character[0]; | ||||
|         const natoWord = getNatoWord(searchChar); | ||||
| 
 | ||||
|         if (isDigit(searchChar)) { | ||||
|           if (useDigitsNames) { | ||||
|             return ` {digit ${searchChar} => ${natoWord}}`; | ||||
|           } | ||||
|           else { | ||||
|             return ` (digit ${character})`; | ||||
|           } | ||||
|         } | ||||
|         if (isPunctuation(searchChar)) { | ||||
|           if (usePunctuationsNames) { | ||||
|             return ` {punctuation ${searchChar} => ${natoWord}}`; | ||||
|           } | ||||
|           else { | ||||
|             return ` (punctuation ${character})`; | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|         if (natoWord) { | ||||
|           return ` ${isUpper ? natoWord.toUpperCase() : natoWord.toLowerCase()}`; | ||||
|         } | ||||
| 
 | ||||
|         return ` (${character})`; | ||||
|       }) | ||||
|     .join(' '); | ||||
|     .trim(); | ||||
| } | ||||
|  | ||||
| @ -1,14 +1,33 @@ | ||||
| <script setup lang="ts"> | ||||
| import { textToNatoAlphabet } from './text-to-nato-alphabet.service'; | ||||
| import { allLanguagesAndCountries } from './text-to-nato-alphabet.constants'; | ||||
| import { useCopy } from '@/composable/copy'; | ||||
| 
 | ||||
| const lang = useStorage('text-to-nato:lang', '(International)'); | ||||
| const input = ref(''); | ||||
| const natoText = computed(() => textToNatoAlphabet({ text: input.value })); | ||||
| const useDigitsNames = useStorage('text-to-nato:digits', false); | ||||
| const usePunctuationsNames = useStorage('text-to-nato:puncts', false); | ||||
| const natoText = computed(() => textToNatoAlphabet({ text: input.value, langOrCountry: lang.value })); | ||||
| const { copy } = useCopy({ source: natoText, text: 'NATO alphabet string copied.' }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div> | ||||
|     <c-select | ||||
|       v-model:value="lang" | ||||
|       :options="allLanguagesAndCountries" | ||||
|       searchable | ||||
|     /> | ||||
| 
 | ||||
|     <div flex justify-center> | ||||
|       <n-form-item label="Use digits pronunciation"> | ||||
|         <n-checkbox v-model:checked="usePunctuationsNames" /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="Use punctuations pronunciation"> | ||||
|         <n-checkbox v-model:checked="useDigitsNames" /> | ||||
|       </n-form-item> | ||||
|     </div> | ||||
| 
 | ||||
|     <c-input-text | ||||
|       v-model:value="input" | ||||
|       label="Your text to convert to NATO phonetic alphabet" | ||||
| @ -19,7 +38,7 @@ const { copy } = useCopy({ source: natoText, text: 'NATO alphabet string copied. | ||||
| 
 | ||||
|     <div v-if="natoText"> | ||||
|       <div mb-2> | ||||
|         Your text in NATO phonetic alphabet | ||||
|         Your text in NATO phonetic alphabet ({{ lang }}) | ||||
|       </div> | ||||
|       <c-card> | ||||
|         {{ natoText }} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user