├── src ├── assets │ └── .gitkeep ├── favicon.ico ├── app │ ├── app.component.css │ ├── language-quiz-question.ts │ ├── app.config.server.ts │ ├── app.routes.ts │ ├── app.config.ts │ ├── app.component.ts │ ├── quiz │ │ ├── quiz.component.css │ │ └── quiz.component.ts │ ├── lang-select │ │ └── lang-select.component.ts │ └── data.service.ts ├── main.ts ├── main.server.ts ├── index.html └── styles.css ├── tsconfig.spec.json ├── tsconfig.app.json ├── .editorconfig ├── .gitignore ├── tsconfig.json ├── docs ├── contributing.md └── code-of-conduct.md ├── .idx └── dev.nix ├── CONTRIBUTING.md ├── package.json ├── README.md ├── angular.json ├── server.ts └── LICENSE /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-gemini/angular-language-learning-sample/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": ["jasmine"] 7 | }, 8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": ["node"] 7 | }, 8 | "files": ["src/main.ts", "src/main.server.ts", "server.ts"], 9 | "include": ["src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | 44 | **/.vscode/ 45 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | :host { 18 | display: flex; 19 | justify-content: center; 20 | margin-top: 20px; 21 | min-width: 420px; 22 | min-height: 463px; 23 | } 24 | -------------------------------------------------------------------------------- /src/app/language-quiz-question.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // data format: 18 | // { phrase: "hola", "options": ["hello", "goodbye", "wait"], "answer": "hello" } 19 | export interface LanguageQuizQuestion { 20 | phrase: string; 21 | options: string[]; 22 | answer: string; 23 | } 24 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { bootstrapApplication } from '@angular/platform-browser'; 18 | import { appConfig } from './app/app.config'; 19 | import { AppComponent } from './app/app.component'; 20 | 21 | bootstrapApplication(AppComponent, appConfig).catch((err) => 22 | console.error(err), 23 | ); 24 | -------------------------------------------------------------------------------- /src/main.server.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { bootstrapApplication } from '@angular/platform-browser'; 18 | import { AppComponent } from './app/app.component'; 19 | import { config } from './app/app.config.server'; 20 | 21 | const bootstrap = () => bootstrapApplication(AppComponent, config); 22 | 23 | export default bootstrap; 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./dist/out-tsc", 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "node", 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "module": "ES2022", 20 | "useDefineForClassFields": false, 21 | "lib": ["ES2022", "dom"] 22 | }, 23 | "angularCompilerOptions": { 24 | "enableI18nLegacyMessageIdFormat": false, 25 | "strictInjectionParameters": true, 26 | "strictInputAccessModifiers": true, 27 | "strictTemplates": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/app.config.server.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; 18 | import { provideServerRendering } from '@angular/platform-server'; 19 | import { appConfig } from './app.config'; 20 | 21 | const serverConfig: ApplicationConfig = { 22 | providers: [provideServerRendering()], 23 | }; 24 | 25 | export const config = mergeApplicationConfig(appConfig, serverConfig); 26 | -------------------------------------------------------------------------------- /src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { Routes } from '@angular/router'; 18 | import { LangSelectComponent } from './lang-select/lang-select.component'; 19 | import { QuizComponent } from './quiz/quiz.component'; 20 | 21 | export const routes: Routes = [ 22 | { 23 | path: '', 24 | component: LangSelectComponent, 25 | }, 26 | { 27 | path: 'quiz/:language', 28 | component: QuizComponent, 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | Angular Language Learning 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { ApplicationConfig } from '@angular/core'; 18 | import { provideRouter, withComponentInputBinding } from '@angular/router'; 19 | 20 | import { routes } from './app.routes'; 21 | import { provideClientHydration } from '@angular/platform-browser'; 22 | 23 | export const appConfig: ApplicationConfig = { 24 | providers: [ 25 | provideRouter(routes, withComponentInputBinding()), 26 | provideClientHydration(), 27 | ], 28 | }; 29 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We would love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our Community Guidelines 22 | 23 | This project follows [Google's Open Source Community 24 | Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code Reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests) 32 | for this purpose. 33 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { Component } from '@angular/core'; 18 | import { RouterOutlet } from '@angular/router'; 19 | import { QuizComponent } from './quiz/quiz.component'; 20 | import { LangSelectComponent } from './lang-select/lang-select.component'; 21 | 22 | @Component({ 23 | selector: 'app-root', 24 | standalone: true, 25 | imports: [RouterOutlet, QuizComponent, LangSelectComponent], 26 | host: { ngSkipHydration: 'true' }, 27 | template: ` 28 |
29 | 30 |
31 | `, 32 | styleUrl: './app.component.css', 33 | }) 34 | export class AppComponent {} 35 | -------------------------------------------------------------------------------- /src/app/quiz/quiz.component.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | header { 18 | margin-bottom: 50px; 19 | font-weight: bold; 20 | display: flex; 21 | justify-content: space-between; 22 | } 23 | label { 24 | display: block; 25 | } 26 | 27 | ul.options { 28 | list-style-type: none; 29 | padding: 0; 30 | margin: 0; 31 | } 32 | 33 | ul.options > li { 34 | font-weight: bold; 35 | } 36 | 37 | .phrase { 38 | font-size: 22pt; 39 | } 40 | 41 | .close, 42 | button.close:hover, 43 | button.close:focus { 44 | color: initial; 45 | width: initial; 46 | border: none; 47 | background: initial; 48 | } 49 | 50 | .message { 51 | max-width: 170px; 52 | } -------------------------------------------------------------------------------- /.idx/dev.nix: -------------------------------------------------------------------------------- 1 | # To learn more about how to use Nix to configure your environment 2 | # see: https://developers.google.com/idx/guides/customize-idx-env 3 | { pkgs, ... }: { 4 | # Which nixpkgs channel to use. 5 | channel = "stable-23.11"; # or "unstable" 6 | # Use https://search.nixos.org/packages to find packages 7 | packages = [ 8 | pkgs.nodejs_18 9 | ]; 10 | # Sets environment variables in the workspace 11 | env = {}; 12 | idx = { 13 | # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" 14 | extensions = [ 15 | "angular.ng-template" 16 | ]; 17 | workspace = { 18 | # Runs when a workspace is first created with this `dev.nix` file 19 | onCreate = { 20 | npm-install = "npm install --no-audit --prefer-offline && npm run build"; 21 | }; 22 | # To run something each time the environment is rebuilt, use the `onStart` hook 23 | onStart = { 24 | start-server = "npm start"; 25 | }; 26 | }; 27 | # Enable previews and customize configuration 28 | previews = { 29 | enable = true; 30 | previews = { 31 | web = { 32 | command = ["npm" "run" "start" "--" "--port" "$PORT" "--host" "0.0.0.0" "--disable-host-check"]; 33 | manager = "web"; 34 | }; 35 | }; 36 | }; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We'd love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our community guidelines 22 | 23 | This project follows 24 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 25 | 26 | ### How to Contribute 27 | 28 | This project is not accepting pull requests at this time. 29 | 30 | Feel free to fork this repository if you wish to make changes. 31 | 32 | ### Code reviews 33 | 34 | All submissions, including submissions by project members, require review. We 35 | use GitHub pull requests for this purpose. Consult 36 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 37 | information on using pull requests. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-language-learning-sample", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "node dist/angular-language-learning-sample/server/server.mjs", 7 | "start:client": "ng serve", 8 | "build": "ng build", 9 | "watch": "ng build --watch --configuration development", 10 | "test": "ng test", 11 | "serve:ssr:angular-language-learning-sample": "node dist/angular-language-learning-sample/server/server.mjs", 12 | "pretty": "npx prettier --write \"./**/*.{js,jsx,mjs,cjs,ts,tsx,json,html,css}\"" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular/animations": "^17.3.0", 17 | "@angular/common": "^17.3.0", 18 | "@angular/compiler": "^17.3.0", 19 | "@angular/core": "^17.3.0", 20 | "@angular/forms": "^17.3.0", 21 | "@angular/platform-browser": "^17.3.0", 22 | "@angular/platform-browser-dynamic": "^17.3.0", 23 | "@angular/platform-server": "^17.3.0", 24 | "@angular/router": "^17.3.0", 25 | "@angular/ssr": "^17.3.5", 26 | "@google/generative-ai": "^0.11.3", 27 | "canvas-confetti": "^1.9.3", 28 | "express": "^4.21.0", 29 | "rxjs": "~7.8.0", 30 | "tslib": "^2.3.0", 31 | "zone.js": "~0.14.3" 32 | }, 33 | "devDependencies": { 34 | "@angular-devkit/build-angular": "^17.3.5", 35 | "@angular/cli": "^17.3.5", 36 | "@angular/compiler-cli": "^17.3.0", 37 | "@types/canvas-confetti": "^1.6.4", 38 | "@types/express": "^4.17.17", 39 | "@types/jasmine": "~5.1.0", 40 | "@types/node": "^18.18.0", 41 | "jasmine-core": "~5.1.0", 42 | "karma": "~6.4.0", 43 | "karma-chrome-launcher": "~3.2.0", 44 | "karma-coverage": "~2.2.0", 45 | "karma-jasmine": "~5.1.0", 46 | "karma-jasmine-html-reporter": "~2.1.0", 47 | "typescript": "~5.4.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | *, 19 | *::before, 20 | *::after { 21 | box-sizing: border-box; 22 | } 23 | 24 | * { 25 | margin: 0; 26 | } 27 | 28 | body { 29 | line-height: 1.5; 30 | -webkit-font-smoothing: antialiased; 31 | } 32 | 33 | img, 34 | picture, 35 | video, 36 | canvas, 37 | svg { 38 | display: block; 39 | max-width: 100%; 40 | } 41 | 42 | input, 43 | button, 44 | textarea, 45 | select { 46 | font: inherit; 47 | } 48 | 49 | p, 50 | h1, 51 | h2, 52 | h3, 53 | h4, 54 | h5, 55 | h6 { 56 | overflow-wrap: break-word; 57 | } 58 | 59 | #root, 60 | #__next { 61 | isolation: isolate; 62 | } 63 | body { 64 | font-family: sans-serif; 65 | background-color: #e8eee9; 66 | } 67 | 68 | button { 69 | display: block; 70 | width: 200px; 71 | border-radius: 5px; 72 | border: solid 1px gray; 73 | background: transparent; 74 | padding: 10px; 75 | margin: 10px; 76 | } 77 | 78 | button:hover, 79 | button:focus { 80 | color: white; 81 | background: black; 82 | } 83 | 84 | .card { 85 | display: flex; 86 | flex-direction: column; 87 | align-items: center; 88 | justify-content: center; 89 | filter: drop-shadow(0 0 0.75rem gray); 90 | padding: 20px 75px 75px 100px; 91 | border-radius: 25px; 92 | background: white; 93 | } 94 | -------------------------------------------------------------------------------- /src/app/lang-select/lang-select.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { Component, inject, signal } from '@angular/core'; 18 | import { FormsModule } from '@angular/forms'; 19 | import { Router } from '@angular/router'; 20 | 21 | @Component({ 22 | selector: 'app-lang-select', 23 | template: ` 24 |

Select a language to practice

25 | @for (lang of languageList; track lang) { 26 | 29 | } 30 |
31 |
32 | 36 | 37 |
38 | `, 39 | styles: ` 40 | .custom-language input { 41 | padding: 10px; 42 | } 43 | .custom-language button { 44 | display: inline; 45 | width: initial; 46 | } 47 | hr { 48 | margin-top: 20px; 49 | margin-bottom: 10px; 50 | } 51 | `, 52 | standalone: true, 53 | imports: [FormsModule], 54 | }) 55 | export class LangSelectComponent { 56 | private readonly router = inject(Router); 57 | protected readonly languageList = [ 58 | { name: 'Spanish' }, 59 | { name: 'Japanese' }, 60 | { name: 'Korean' }, 61 | { name: 'Hindi' }, 62 | ]; 63 | protected readonly customLanguage = signal(''); 64 | 65 | protected startQuiz(lang: string) { 66 | this.router.navigate(['quiz', lang]); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular language learning sample 2 | Developer sample written in Angular, demonstrating how to use system instruction and prompting 3 | in the Gemini API to collaboratively and iteratively create to create quiz questions in 4 | supported languages 5 | 6 | 7 | 8 | 9 | 10 | Open in IDX 11 | 12 | 13 | 14 | ## Setup & Getting Started 15 | This example uses [Angular](http://angular.dev) and [Gemini API](http://ai.google.dev). 16 | 17 | ### Before you start 18 | Your system needs to be configured to run [Angular](https://angular.dev/tools/cli/setup-local#dependencies). 19 | 20 | You need Gemini API key in order to run this demo. Here's how: 21 | 1. Launch [Google AI Studio](https://aistudio.google.com/) 22 | 1. Click "Get API Key" 23 | 24 | > Caution: **Using the Google AI SDK for JavaScript directly from a client-side 25 | app is recommended for prototyping only.** For non-prototyping use cases, we 26 | strongly recommend that you call the Google AI Gemini API only server-side to 27 | keep your API key safe. If you embed your API key directly in your JavaScript 28 | app or fetch it remotely at runtime, you risk potentially exposing your API key 29 | to malicious actors. 30 | 31 | ### Getting the code 32 | You have two options to get this code: 33 | * [Open it in IDX](https://idx.google.com/import?url=https://github.com/google-gemini/angular-language-learning-sample) 34 | * Clone this repo to your local machine. 35 | 36 | ### Configure your environment 37 | To run this application, you can either 38 | * hard code your API key in `server.ts` 39 | * set it as an environment variable called `API_KEY` 40 | 41 | 42 | ### Starting the application 43 | > Note: Do not use `ng serve` as this sample uses server side rendering (SSR). 44 | 1. Build the example with `ng build` 45 | 2. Run the demo with `npm start` 46 | 3. Navigate to `http://localhost:4000/` 47 | 48 | That's it, have fun! 49 | 50 | ### Making local changes 51 | > Note: Do not use `ng serve` as this sample uses server side rendering (SSR). 52 | 1. Update the files. 53 | 2. Stop the server 54 | 3. Run `ng build` 55 | 4. Run `npm start` -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-language-learning-sample": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:application", 15 | "options": { 16 | "outputPath": "dist/angular-language-learning-sample", 17 | "index": "src/index.html", 18 | "browser": "src/main.ts", 19 | "polyfills": ["zone.js"], 20 | "tsConfig": "tsconfig.app.json", 21 | "assets": ["src/favicon.ico", "src/assets"], 22 | "styles": ["src/styles.css"], 23 | "scripts": [], 24 | "server": "src/main.server.ts", 25 | "prerender": true, 26 | "ssr": { 27 | "entry": "server.ts" 28 | } 29 | }, 30 | "configurations": { 31 | "production": { 32 | "budgets": [ 33 | { 34 | "type": "initial", 35 | "maximumWarning": "500kb", 36 | "maximumError": "1mb" 37 | }, 38 | { 39 | "type": "anyComponentStyle", 40 | "maximumWarning": "2kb", 41 | "maximumError": "4kb" 42 | } 43 | ], 44 | "outputHashing": "all" 45 | }, 46 | "development": { 47 | "optimization": false, 48 | "extractLicenses": false, 49 | "sourceMap": true 50 | } 51 | }, 52 | "defaultConfiguration": "production" 53 | }, 54 | "serve": { 55 | "builder": "@angular-devkit/build-angular:dev-server", 56 | "configurations": { 57 | "production": { 58 | "buildTarget": "angular-language-learning-sample:build:production" 59 | }, 60 | "development": { 61 | "buildTarget": "angular-language-learning-sample:build:development" 62 | } 63 | }, 64 | "defaultConfiguration": "development" 65 | }, 66 | "extract-i18n": { 67 | "builder": "@angular-devkit/build-angular:extract-i18n", 68 | "options": { 69 | "buildTarget": "angular-language-learning-sample:build" 70 | } 71 | }, 72 | "test": { 73 | "builder": "@angular-devkit/build-angular:karma", 74 | "options": { 75 | "polyfills": ["zone.js", "zone.js/testing"], 76 | "tsConfig": "tsconfig.spec.json", 77 | "assets": ["src/favicon.ico", "src/assets"], 78 | "styles": ["src/styles.css"], 79 | "scripts": [] 80 | } 81 | } 82 | } 83 | } 84 | }, 85 | "cli": { 86 | "analytics": "7444fc98-1b17-4afd-9d62-b99cc0ed4745" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/app/data.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { Injectable } from '@angular/core'; 18 | import { LanguageQuizQuestion } from './language-quiz-question'; 19 | 20 | @Injectable({ 21 | providedIn: 'root', 22 | }) 23 | export class DataService { 24 | async getQuestions(lang: string): Promise { 25 | try { 26 | const data = await fetch( 27 | `/api/ask-gemini?lang=${lang}` 28 | ).then((response) => { 29 | console.log(response); 30 | console.log(response.body); 31 | return response.json(); 32 | }); 33 | 34 | let questions = backupQuestions; 35 | 36 | if (data.response) { 37 | questions = this.parseGeminiResponse(data.response); 38 | } 39 | 40 | return Promise.resolve({ 41 | questions, 42 | message: data.message, 43 | }); 44 | } catch (e) { 45 | console.error('Unexpected error fetching from server', e); 46 | console.log('Did you use `ng serve` instead of `npm start`?'); 47 | return Promise.resolve({ 48 | questions: backupQuestions, 49 | message: 'Using offline fallback language.' + 50 | ' See JavaScript console for details.' 51 | }); 52 | } 53 | } 54 | 55 | parseGeminiResponse(response: string): LanguageQuizQuestion[] { 56 | let dataObject = JSON.parse(response); 57 | 58 | if (Array.isArray(dataObject)) { 59 | return dataObject; 60 | } else { 61 | return []; 62 | } 63 | } 64 | } 65 | interface QuizQueryResponse { 66 | questions: LanguageQuizQuestion[]; 67 | message: string; 68 | } 69 | 70 | const backupQuestions = [ 71 | { phrase: 'perro', options: ['dog', 'cat', 'bird'], answer: 'dog' }, 72 | { phrase: 'gato', options: ['fish', 'cat', 'horse'], answer: 'cat' }, 73 | { phrase: 'casa', options: ['car', 'house', 'tree'], answer: 'house' }, 74 | { phrase: 'libro', options: ['book', 'pencil', 'door'], answer: 'book' }, 75 | { phrase: 'agua', options: ['water', 'milk', 'juice'], answer: 'water' }, 76 | { phrase: 'comida', options: ['food', 'chair', 'table'], answer: 'food' }, 77 | { phrase: 'hombre', options: ['man', 'woman', 'child'], answer: 'man' }, 78 | { phrase: 'mujer', options: ['girl', 'woman', 'boy'], answer: 'woman' }, 79 | { phrase: 'niño', options: ['child', 'adult', 'baby'], answer: 'child' }, 80 | { phrase: 'grande', options: ['small', 'big', 'medium'], answer: 'big' }, 81 | { phrase: 'pequeño', options: ['large', 'small', 'tall'], answer: 'small' }, 82 | { phrase: 'caliente', options: ['hot', 'cold', 'warm'], answer: 'hot' }, 83 | { phrase: 'frío', options: ['cold', 'hot', 'cool'], answer: 'cold' }, 84 | { phrase: 'feliz', options: ['sad', 'happy', 'angry'], answer: 'happy' }, 85 | { phrase: 'triste', options: ['happy', 'excited', 'sad'], answer: 'sad' }, 86 | { 87 | phrase: 'cansado', 88 | options: ['tired', 'energetic', 'sleepy'], 89 | answer: 'tired', 90 | }, 91 | { 92 | phrase: 'aburrido', 93 | options: ['bored', 'excited', 'interested'], 94 | answer: 'bored', 95 | }, 96 | { phrase: 'bueno', options: ['bad', 'good', 'okay'], answer: 'good' }, 97 | { phrase: 'malo', options: ['good', 'bad', 'great'], answer: 'bad' }, 98 | { phrase: 'alto', options: ['short', 'tall', 'medium'], answer: 'tall' }, 99 | { phrase: 'bajo', options: ['tall', 'short', 'wide'], answer: 'short' }, 100 | { phrase: 'rápido', options: ['slow', 'fast', 'quick'], answer: 'fast' }, 101 | { phrase: 'lento', options: ['fast', 'slow', 'steady'], answer: 'slow' }, 102 | { phrase: 'viejo', options: ['young', 'old', 'new'], answer: 'old' }, 103 | ]; 104 | -------------------------------------------------------------------------------- /server.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { APP_BASE_HREF } from '@angular/common'; 18 | import { CommonEngine } from '@angular/ssr'; 19 | import express from 'express'; 20 | import { fileURLToPath } from 'node:url'; 21 | import { dirname, join, resolve } from 'node:path'; 22 | import bootstrap from './src/main.server'; 23 | import { 24 | GoogleGenerativeAI, 25 | GenerateContentRequest, 26 | } from '@google/generative-ai'; 27 | 28 | // The Express app is exported so that it can be used by serverless Functions. 29 | export function app(): express.Express { 30 | const server = express(); 31 | const serverDistFolder = dirname(fileURLToPath(import.meta.url)); 32 | const browserDistFolder = resolve(serverDistFolder, '../browser'); 33 | const indexHtml = join(serverDistFolder, 'index.server.html'); 34 | 35 | // Update this line to add your API_KEY if you are not using env variables 36 | const GEMINI_API_KEY = process.env['API_KEY'] || ''; 37 | const commonEngine = new CommonEngine(); 38 | 39 | const genAI = new GoogleGenerativeAI(GEMINI_API_KEY); 40 | const model = genAI.getGenerativeModel({ model: 'gemini-1.5-pro-latest' }); 41 | model.generationConfig.responseMimeType = 'application/json'; 42 | const DEFAULT_LANG = 'spanish'; 43 | 44 | server.set('view engine', 'html'); 45 | server.set('views', browserDistFolder); 46 | 47 | // Define Express Rest API endpoints 48 | server.get('/api/ask-gemini', async (req, res) => { 49 | if (!GEMINI_API_KEY) { 50 | return res.status(500).send({ 51 | message: `Please provide an API key for Gemini, using default ${DEFAULT_LANG} translations.`, 52 | }); 53 | } 54 | 55 | let language = req.query['lang'] ? req.query['lang'] as string : DEFAULT_LANG; 56 | 57 | try { 58 | const prompt = `Your task is to generate 25 vocabulary words for learning ${language}. 59 | Provide the ${language} translation, with 1 correct english translation and 2 incorrect english translations. 60 | 61 | input: generate 25 vocaulary words for learning spanish 62 | output: [{"phrase": "hola", "options"=["goodbye", "wait", "hello"], "answer": "hello"}, {"phrase": "adios", "options":["good morning", "see you later", "car"], "answer": "see you later"}, {"phrase": "gracias", "options":["shoes", "thank you", "comb"], "answer": "thank you"}] 63 | `; 64 | const request: GenerateContentRequest = { 65 | contents: [{ role: 'user', parts: [{ text: prompt }] }], 66 | tools: [], 67 | }; 68 | const results = await model.generateContent(request); 69 | 70 | return res 71 | .status(200) 72 | .send({ response: results.response.text(), usingDefault: false }); 73 | } catch (e) { 74 | return res.status(500).send({ 75 | message: `Unable to generate the practice questions for ${language}, using default ${DEFAULT_LANG} translations.`, 76 | }); 77 | } 78 | }); 79 | 80 | // Serve static files from /browser 81 | server.get( 82 | '*.*', 83 | express.static(browserDistFolder, { 84 | maxAge: '1y', 85 | }) 86 | ); 87 | 88 | // All regular routes use the Angular engine 89 | server.get('*', (req, res, next) => { 90 | const { protocol, originalUrl, baseUrl, headers } = req; 91 | 92 | commonEngine 93 | .render({ 94 | bootstrap, 95 | documentFilePath: indexHtml, 96 | url: `${protocol}://${headers.host}${originalUrl}`, 97 | publicPath: browserDistFolder, 98 | providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], 99 | }) 100 | .then((html) => res.send(html)) 101 | .catch((err) => next(err)); 102 | }); 103 | 104 | return server; 105 | } 106 | 107 | function run(): void { 108 | const port = process.env['PORT'] || process.argv[3] || 4000; 109 | 110 | // Start up the Node server 111 | const server = app(); 112 | server.listen(port, () => { 113 | console.log(`Node Express server listening on http://localhost:${port}`); 114 | }); 115 | } 116 | 117 | run(); 118 | 119 | -------------------------------------------------------------------------------- /docs/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, education, socio-economic status, nationality, personal appearance, 10 | race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | This Code of Conduct also applies outside the project spaces when the Project 56 | Steward has a reasonable belief that an individual's behavior may have a 57 | negative impact on the project or its community. 58 | 59 | ## Conflict Resolution 60 | 61 | We do not believe that all conflict is bad; healthy debate and disagreement 62 | often yield positive results. However, it is never okay to be disrespectful or 63 | to engage in behavior that violates the project’s code of conduct. 64 | 65 | If you see someone violating the code of conduct, you are encouraged to address 66 | the behavior directly with those involved. Many issues can be resolved quickly 67 | and easily, and this gives people more control over the outcome of their 68 | dispute. If you are unable to resolve the matter for any reason, or if the 69 | behavior is threatening or harassing, report it. We are dedicated to providing 70 | an environment where participants feel welcome and safe. 71 | 72 | Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the 73 | Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to 74 | receive and address reported violations of the code of conduct. They will then 75 | work with a committee consisting of representatives from the Open Source 76 | Programs Office and the Google Open Source Strategy team. If for any reason you 77 | are uncomfortable reaching out to the Project Steward, please email 78 | opensource@google.com. 79 | 80 | We will investigate every complaint, but you may not receive a direct response. 81 | We will use our discretion in determining when and how to follow up on reported 82 | incidents, which may range from not taking action to permanent expulsion from 83 | the project and project-sponsored spaces. We will notify the accused of the 84 | report and provide them an opportunity to discuss it before any action is taken. 85 | The identity of the reporter will be omitted from the details of the report 86 | supplied to the accused. In potentially harmful situations, such as ongoing 87 | harassment or threats to anyone's safety, we may take action without notice. 88 | 89 | ## Attribution 90 | 91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 92 | available at 93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct/ 94 | -------------------------------------------------------------------------------- /src/app/quiz/quiz.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { 18 | Component, 19 | OnInit, 20 | computed, 21 | effect, 22 | inject, 23 | input, 24 | signal, 25 | } from '@angular/core'; 26 | import { LanguageQuizQuestion } from '../language-quiz-question'; 27 | import { DataService } from '../data.service'; 28 | import confetti from 'canvas-confetti'; 29 | import { Router } from '@angular/router'; 30 | 31 | @Component({ 32 | selector: 'app-quiz', 33 | standalone: true, 34 | host: { ngSkipHydration: 'true' }, 35 | template: ` 36 |
37 | @if (isLoading()) { 38 |

Get ready to practice...

39 | } @else { 40 |
41 |
42 | 45 | 50 | {{ score() / languageQuizQuestionList().length }} 51 | 52 |
53 |
54 | 55 |
56 |
57 |

{{ message() }} 

58 |
59 | @if (languageQuizQuestionList()[currentTranslationIndex()]) { 60 |

61 | {{ languageQuizQuestionList()[currentTranslationIndex()].phrase }} 62 |

63 |
    64 | @if (currentQuestion()) { 65 | @for (option of currentQuestion().options; track option) { 66 |
  • 67 | 70 |
  • 71 | } 72 | } 73 |
74 | } 75 | @if (isGameOver()) { 76 |
77 |

Hooray! Well, done!

78 | 79 |
80 | } 81 |
82 | } 83 |
84 | `, 85 | styleUrl: './quiz.component.css', 86 | }) 87 | export class QuizComponent implements OnInit { 88 | private readonly router = inject(Router); 89 | private readonly dataService = inject(DataService); 90 | 91 | protected readonly language = input.required(); 92 | protected readonly score = signal(0); 93 | protected readonly isLoading = signal(true); 94 | protected readonly message = signal(''); 95 | protected readonly currentTranslationIndex = signal(0); 96 | protected readonly languageQuizQuestionList = signal( 97 | [], 98 | ); 99 | protected readonly isGameOver = computed(() => { 100 | return ( 101 | this.score() === this.languageQuizQuestionList().length && 102 | this.score() > 0 103 | ); 104 | }); 105 | 106 | protected readonly currentQuestion = computed( 107 | () => this.languageQuizQuestionList()[this.currentTranslationIndex()], 108 | ); 109 | 110 | constructor() { 111 | effect(() => { 112 | if (this.isGameOver()) confetti(); 113 | }); 114 | } 115 | 116 | ngOnInit(): void { 117 | const language = this.language() || 'spanish'; 118 | this.dataService.getQuestions(language).then((response) => { 119 | this.languageQuizQuestionList.set(response.questions); 120 | this.message.set(response.message); 121 | this.isLoading.set(false); 122 | }); 123 | } 124 | 125 | protected nextQuestion() { 126 | this.message.set(''); 127 | this.currentTranslationIndex.update((idx) => { 128 | return idx + 1; 129 | }); 130 | } 131 | 132 | protected checkAnswer(answer: string) { 133 | if (this.currentQuestion().answer === answer) { 134 | this.score.update((score) => score + 1); 135 | this.nextQuestion(); 136 | } else { 137 | this.message.set('Oops, try again!'); 138 | } 139 | } 140 | 141 | protected reset() { 142 | this.score.set(0); 143 | this.currentTranslationIndex.set(0); 144 | } 145 | 146 | protected quit() { 147 | this.router.navigateByUrl(''); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------