├── assets ├── i18n │ ├── es │ │ └── translation.json │ └── en │ │ └── translation.json └── bitmapfonts │ ├── iceicebaby.png │ ├── helvetica_regular.png │ ├── iceicebaby.xml │ └── helvetica_regular.xml ├── test ├── README.md ├── entry.ts └── com │ └── koreez │ └── plugin │ └── plugin.test.ts ├── static ├── assets │ ├── i18n │ │ ├── en │ │ │ └── translation.json │ │ └── es │ │ │ └── translation.json │ └── bitmapfonts │ │ ├── helvetica_regular.png │ │ └── helvetica_regular.xml ├── styles │ └── main.css ├── index.html └── manifest.json ├── src ├── com │ └── koreez │ │ └── plugin │ │ ├── i18n │ │ ├── I18nScene.ts │ │ ├── textExtensions.ts │ │ └── Ii18n.ts │ │ └── I18nPlugin.ts └── index.ts ├── CHANGELOG.md ├── .prettierrc ├── tslint.test.json ├── tsconfig.test.json ├── .gitignore ├── .publishrc ├── .editorconfig ├── config ├── .eslintrc.js ├── webpack.parts.config.js └── webpack.config.js ├── .npmignore ├── .travis.yml ├── tsconfig.json ├── LICENSE ├── .istanbul.yml ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── webpack.example.config.js ├── CONTRIBUTING.md ├── tslint.json ├── example └── index.ts ├── webpack.config.js ├── karma.conf.js ├── CODE_OF_CONDUCT.md ├── package.json └── README.md /assets/i18n/es/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Hola", 3 | "world": "Mundo" 4 | } 5 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Plugin Testing 2 | 3 | Unit tests are written using Mocha, Chai and Sinon. -------------------------------------------------------------------------------- /static/assets/i18n/en/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Hello", 3 | "world": "World" 4 | } 5 | -------------------------------------------------------------------------------- /static/assets/i18n/es/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Hola", 3 | "world": "Mundo" 4 | } 5 | -------------------------------------------------------------------------------- /assets/bitmapfonts/iceicebaby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-i18n-plugin/HEAD/assets/bitmapfonts/iceicebaby.png -------------------------------------------------------------------------------- /assets/bitmapfonts/helvetica_regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-i18n-plugin/HEAD/assets/bitmapfonts/helvetica_regular.png -------------------------------------------------------------------------------- /assets/i18n/en/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Hello", 3 | "world": "World", 4 | "interpolations": "Interpolations: {{0}}" 5 | } 6 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/i18n/I18nScene.ts: -------------------------------------------------------------------------------- 1 | import { Ii18n } from "./Ii18n"; 2 | 3 | export interface I18nScene extends Phaser.Scene { 4 | i18n: Ii18n; 5 | } 6 | -------------------------------------------------------------------------------- /static/assets/bitmapfonts/helvetica_regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-i18n-plugin/HEAD/static/assets/bitmapfonts/helvetica_regular.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Phaser3 i18n Plugin: 2 | 3 | ## Phaser3 i18n Plugin 2.0.6 4 | 5 | ### v2.0.6 6 | 7 | - fixed dynamic bitmap text bug 8 | 9 | ### v2.0.5 10 | 11 | - added i18next export 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "tabWidth": 4, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": false, 7 | "trailingComma": "none", 8 | "bracketSpacing": true 9 | } 10 | -------------------------------------------------------------------------------- /test/entry.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | import "bluebird/js/browser/bluebird"; 5 | import "es6-map/implement"; 6 | import "es6-symbol/implement"; 7 | -------------------------------------------------------------------------------- /tslint.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "./tslint.json" 4 | ], 5 | "rules": { 6 | "no-implicit-dependencies": false, 7 | "no-reference": false, 8 | "no-submodule-imports": false 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib-test" 5 | }, 6 | "include": ["./src/**/*.ts", "./test/**/*.ts"], 7 | "files": ["./types/phaser.d.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .nyc_output 3 | coverage 4 | dist/lib 5 | dist-test 6 | lib 7 | lib-test 8 | node_modules 9 | *.as 10 | *.iml 11 | src/**/*.js 12 | src/**/*.js.map 13 | test/**/*.js 14 | test/**/*.js.map 15 | npm-debug.log 16 | yarn-error.log 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /static/styles/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | width: 100%; 3 | height: 100%; 4 | margin: 0px; 5 | padding: 0px; 6 | } 7 | 8 | body { 9 | height: inherit; 10 | width: inherit; 11 | margin: 0px; 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | } 16 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Phaser Boilerplate 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.publishrc: -------------------------------------------------------------------------------- 1 | { 2 | "validations": { 3 | "vulnerableDependencies": true, 4 | "uncommittedChanges": true, 5 | "untrackedFiles": true, 6 | "sensitiveData": false, 7 | "branch": "master", 8 | "gitTag": true 9 | }, 10 | "confirm": true, 11 | "publishTag": "latest --access public", 12 | "prePublishScript": false, 13 | "postPublishScript": false 14 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | indent_style = space 8 | indent_size = 4 9 | 10 | [*.{ts}] 11 | indent_style = space 12 | indent_size = 4 13 | 14 | [*.{js,json}] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [{.codeclimate.yml,.eslintignore,.eslintrc,.istanbul.yml,.publishrc,.travis.yml}] 19 | indent_style = space 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /config/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const settings = { 2 | env: { 3 | browser: false, 4 | node: true, 5 | }, 6 | extends: 'standard', 7 | rules: { 8 | 'comma-dangle': [ 9 | 'error', 10 | { 11 | arrays: 'always-multiline', 12 | objects: 'always-multiline', 13 | imports: 'always-multiline', 14 | exports: 'always-multiline', 15 | functions: 'always-multiline', 16 | }, 17 | ], 18 | }, 19 | } 20 | 21 | module.exports = settings 22 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.nyc_output 3 | /.vscode 4 | /coverage 5 | /dist 6 | /dist-test 7 | /docs 8 | /lib-test 9 | /not-ported-extensions 10 | /src 11 | /test 12 | /transpiled-code 13 | /typings 14 | CODE_OF_CONDUCT.md 15 | CONTRIBUTING.md 16 | ISSUE_TEMPLATE.md 17 | PULL_REQUEST_TEMPLATE.md 18 | karma.conf.js 19 | tsconfig.json 20 | tsconfig.test.json 21 | tslint.json 22 | tslint.test.json 23 | webpack.config.js 24 | yarn.lock 25 | .editorconfig 26 | .gitignore 27 | .istanbul.yml 28 | .npmignore 29 | .prettierrc 30 | .publishrc 31 | .travis.yml -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // Copyright (c) 2018 Koreez LLC. All Rights Reserved. 3 | // 4 | // NOTICE: You are permitted to use, modify, and distribute this file 5 | // in accordance with the terms of the license agreement accompanying it. 6 | // ------------------------------------------------------------------------------ 7 | 8 | /** 9 | * I18nPlugin 10 | */ 11 | import i18next from "i18next"; 12 | export { I18nScene } from "./com/koreez/plugin/i18n/I18nScene"; 13 | export { I18nPlugin } from "./com/koreez/plugin/I18nPlugin"; 14 | export { i18next }; 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | env: 5 | global: 6 | - CC_TEST_REPORTER_ID=71e455189fdcb3925ba38e620c090f72e7734ab75219b2a39fbaaad0021daab7 7 | before_install: 8 | - export CHROME_BIN=chromium-browser 9 | - export DISPLAY=:99.0 10 | - sh -e /etc/init.d/xvfb start 11 | - yarn config set registry "https://registry.npmjs.org" 12 | - yarn global add greenkeeper-lockfile@1 13 | - yarn global add codeclimate-test-reporter 14 | before_script: 15 | - greenkeeper-lockfile-update 16 | script: 17 | - yarn test 18 | after_script: 19 | - greenkeeper-lockfile-upload 20 | after_success: 21 | - codeclimate-test-reporter < coverage/lcov.info 22 | -------------------------------------------------------------------------------- /static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Your Game", 3 | "short_name": "Your Game", 4 | "start_url": "index.html", 5 | "display": "standalone", 6 | "orientation": "landscape", 7 | "icons": [ 8 | { 9 | "src": "icon.png", 10 | "sizes": "512x512", 11 | "type": "image/png" 12 | }, 13 | { 14 | "src": "launcher-icon-2x.png", 15 | "sizes": "96x96", 16 | "type": "image/png" 17 | }, 18 | { 19 | "src": "launcher-icon-3x.png", 20 | "sizes": "144x144", 21 | "type": "image/png" 22 | }, 23 | { 24 | "src": "launcher-icon-4x.png", 25 | "sizes": "192x192", 26 | "type": "image/png" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "target": "es5", 5 | "lib": ["es7", "dom"], 6 | "sourceMap": true, 7 | "inlineSources": true, 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "emitDecoratorMetadata": true, 12 | "removeComments": true, 13 | "strict": false, 14 | "noImplicitAny": true, 15 | "strictNullChecks": false, 16 | "noImplicitThis": false, 17 | "alwaysStrict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": false, 20 | "noImplicitReturns": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["./src/**/*", "./types/**/*", "./plugins/**/*"] 24 | } 25 | -------------------------------------------------------------------------------- /config/webpack.parts.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const CleanWebpackPlugin = require('clean-webpack-plugin') 3 | const GitRevisionPlugin = require('git-revision-webpack-plugin') 4 | 5 | exports.cleanup = paths => ({ 6 | plugins: [ 7 | new CleanWebpackPlugin(paths, { root: process.cwd(), verbose: false }), 8 | ], 9 | }) 10 | 11 | exports.loadJs = ({ options }) => ({ 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.ts?$/, 16 | exclude: ['/node_modules/', '/lib/'], 17 | use: [ 18 | { 19 | loader: 'ts-loader', 20 | options: options, 21 | }, 22 | ], 23 | }, 24 | ], 25 | }, 26 | }) 27 | 28 | exports.sourceMaps = method => ({ 29 | devtool: method, 30 | }) 31 | 32 | exports.attachRevision = () => ({ 33 | plugins: [ 34 | new webpack.BannerPlugin({ 35 | banner: new GitRevisionPlugin().version(), 36 | }), 37 | ], 38 | }) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Koreez LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | verbose: false 2 | instrumentation: 3 | root: lib 4 | extensions: 5 | - .ts 6 | # default-excludes: true 7 | # excludes: [] 8 | # variable: __coverage__ 9 | # compact: true 10 | # preserve-comments: false 11 | # complete-copy: false 12 | # save-baseline: false 13 | # baseline-file: ./coverage/coverage-baseline.raw.json 14 | # include-all-sources: false 15 | # include-pid: false 16 | # es-modules: true 17 | # auto-wrap: true 18 | # reporting: 19 | # print: summary 20 | # reports: 21 | # - lcov 22 | # dir: ./coverage 23 | # summarizer: pkg 24 | # report-config: {} 25 | # watermarks: 26 | # statements: [50, 80] 27 | # functions: [50, 80] 28 | # branches: [50, 80] 29 | # lines: [50, 80] 30 | # hooks: 31 | # hook-run-in-context: false 32 | # post-require-hook: null 33 | # handle-sigint: false 34 | # check: 35 | # global: 36 | # statements: 0 37 | # lines: 0 38 | # branches: 0 39 | # functions: 0 40 | # excludes: [] 41 | # each: 42 | # statements: 0 43 | # lines: 0 44 | # branches: 0 45 | # functions: 0 46 | # excludes: [] 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Expected Behavior 4 | 5 | 6 | 7 | ## Current Behavior 8 | 9 | 10 | 11 | ## Possible Solution 12 | 13 | 14 | 15 | ## Steps to Reproduce (for bugs) 16 | 17 | 18 | 1. 19 | 2. 20 | 3. 21 | 4. 22 | 23 | ## Context 24 | 25 | 26 | 27 | ## Your Environment 28 | 29 | * Version used: 30 | * Environment name and version (e.g. Chrome 39, node.js 5.4): 31 | * Operating System and version (desktop or mobile): 32 | * Link to your project: 33 | 34 | # Stack trace 35 | 36 | -------------------------------------------------------------------------------- /webpack.example.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const path = require("path"); 3 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 4 | const ConcatPlugin = require("webpack-concat-plugin"); 5 | 6 | const concatPluginConfigGenerator = (name, files) => { 7 | return { 8 | uglify: false, 9 | sourceMap: false, 10 | name: name, 11 | fileName: "[name].js", 12 | filesToConcat: files, 13 | injectType: "none" 14 | }; 15 | }; 16 | 17 | module.exports = (function(options) { 18 | return { 19 | mode: "development", 20 | 21 | entry: { 22 | main: path.resolve("example/index.ts") 23 | }, 24 | 25 | output: { 26 | path: __dirname + "/dist", 27 | filename: "bundle.js" 28 | }, 29 | 30 | devtool: "source-map", 31 | 32 | module: { 33 | rules: [{ test: /\.ts$/, loader: "ts-loader" }] 34 | }, 35 | 36 | plugins: [ 37 | new HtmlWebpackPlugin({ 38 | template: path.resolve("static/index.html"), 39 | inject: false 40 | }), 41 | 42 | new ConcatPlugin(concatPluginConfigGenerator("phaser", [path.resolve(__dirname, "./node_modules/phaser/dist/phaser.js")])) 43 | ], 44 | 45 | resolve: { 46 | extensions: [".ts", ".js", ".json"] 47 | } 48 | }; 49 | })(); 50 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to phaser3-i18n-plugin 2 | 3 | ## Setup 4 | 5 | 1 - Clone your fork of the repository: 6 | 7 | ``` 8 | $ git clone https://github.com/koreezgames/phaser3-i18n-plugin.git 9 | ``` 10 | 11 | 2 - Install npm dependencies using yarn: 12 | 13 | ``` 14 | $ yarn install 15 | ``` 16 | 17 | 3 - Run start process 18 | 19 | ``` 20 | $ yarn run start 21 | ``` 22 | 23 | 4 - Run test process 24 | 25 | ``` 26 | $ yarn test 27 | ``` 28 | 29 | ## Guidelines 30 | 31 | - Please try to [combine multiple commits before 32 | pushing](http://stackoverflow.com/questions/6934752/combining-multiple-commits-before-pushing-in-git). 33 | 34 | - Please use `TDD` when fixing bugs. This means that you should write a unit 35 | test that fails because it reproduces the issue, then fix the issue and finally run 36 | the test to ensure that the issue has been resolved. This helps us to prevent 37 | fixed bugs from happening again in the future. 38 | 39 | - Always format your code using `yarn run autoformat`. 40 | 41 | - Please keep the test coverage at 100%. Write additional unit test if 42 | necessary. 43 | 44 | - Please create an issue before sending a PR if your commit is going to change the 45 | public interface of the package or it includes significant architecture 46 | changes. 47 | 48 | - Feel free to ask for help from other members of the Koreez team via the 49 | [github issues](https://github.com/koreezgames/phaser3-i18n-plugin/issues). 50 | -------------------------------------------------------------------------------- /config/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const merge = require('webpack-merge') 3 | const packagejson = require('../package.json') 4 | const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin') 5 | 6 | const parts = require('./webpack.parts.config') 7 | 8 | let main = packagejson.main 9 | main = main.replace(/^.*[\\/]/, '') 10 | 11 | const libraryName = main.substring(0, main.lastIndexOf('.')) 12 | 13 | const paths = { 14 | base: path.resolve('src'), 15 | app: path.resolve('src/index.ts'), 16 | dist: path.resolve('lib'), 17 | } 18 | 19 | const libConfig = merge([ 20 | { 21 | target: 'web', 22 | context: paths.base, 23 | entry: { 24 | app: paths.app, 25 | }, 26 | output: { 27 | library: libraryName, 28 | filename: libraryName + '.js', 29 | libraryExport: 'default', 30 | libraryTarget: 'umd', 31 | umdNamedDefine: true, 32 | path: paths.dist, 33 | }, 34 | externals: { 35 | phaser: 'phaser', 36 | }, 37 | resolve: { 38 | modules: [path.resolve('./node_modules'), path.resolve('./src')], 39 | extensions: ['.json', '.js', '.ts'], 40 | }, 41 | plugins: [new CaseSensitivePathsPlugin()], 42 | }, 43 | 44 | parts.loadJs({}), 45 | 46 | parts.sourceMaps('source-map'), 47 | 48 | parts.cleanup([paths.dist]), 49 | 50 | parts.attachRevision(), 51 | ]) 52 | 53 | module.exports = env => { 54 | const config = merge(libConfig) 55 | 56 | return config 57 | } 58 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-config-prettier"], 3 | "rules": { 4 | "ban-types": [false], 5 | "class-name": true, 6 | "comment-format": [true, "check-space"], 7 | "curly": true, 8 | "forin": true, 9 | "label-position": true, 10 | "member-access": true, 11 | "member-ordering": [ 12 | true, 13 | { 14 | "order": [ 15 | "static-field", 16 | "instance-field", 17 | "constructor", 18 | "public-instance-method", 19 | "protected-instance-method", 20 | "private-instance-method" 21 | ] 22 | } 23 | ], 24 | "no-angle-bracket-type-assertion": false, 25 | "no-arg": true, 26 | "no-bitwise": true, 27 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 28 | "no-construct": true, 29 | "no-debugger": true, 30 | "no-duplicate-variable": true, 31 | "no-empty": false, 32 | "no-eval": true, 33 | "no-inferrable-types": false, 34 | "no-shadowed-variable": true, 35 | "no-string-literal": true, 36 | "no-switch-case-fall-through": false, 37 | "no-this-assignment": false, 38 | "no-unused-expression": true, 39 | "no-use-before-declare": true, 40 | "no-var-keyword": true, 41 | "object-literal-sort-keys": true, 42 | "ordered-imports": false, 43 | "only-arrow-functions": [false], 44 | "prefer-const": false, 45 | "radix": true, 46 | "trailing-comma": [false], 47 | "triple-equals": [true, "allow-null-check"], 48 | "variable-name": false 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | 7 | ## Related Issue 8 | 9 | 10 | 11 | 12 | 13 | 14 | ## Motivation and Context 15 | 16 | 17 | 18 | ## How Has This Been Tested? 19 | 20 | 21 | 22 | 23 | 24 | ## Types of changes 25 | 26 | 27 | 28 | - [ ] Updated docs / Refactor code / Added a tests case (non-breaking change) 29 | - [ ] Bug fix (non-breaking change which fixes an issue) 30 | - [ ] New feature (non-breaking change which adds functionality) 31 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 32 | 33 | ## Checklist: 34 | 35 | 36 | 37 | 38 | - [ ] My code follows the code style of this project. 39 | - [ ] My change requires a change to the documentation. 40 | - [ ] I have updated the documentation accordingly. 41 | - [ ] I have read the **CONTRIBUTING** document. 42 | - [ ] I have added tests to cover my changes. 43 | - [ ] All new and existing tests passed. 44 | -------------------------------------------------------------------------------- /example/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import "phaser"; 3 | import { I18nPlugin } from "../src/com/koreez/plugin/I18nPlugin"; 4 | 5 | export default class Game extends Phaser.Game { 6 | public centerX: number; 7 | public centerY: number; 8 | constructor(config: GameConfig) { 9 | super(config); 10 | } 11 | } 12 | 13 | function preload() { 14 | this.load.bitmapFont("helvetica_regular", "assets/bitmapfonts/helvetica_regular.png", "assets/bitmapfonts/helvetica_regular.xml"); 15 | this.load.bitmapFont("iceicebaby", "assets/bitmapfonts/iceicebaby.png", "assets/bitmapfonts/iceicebaby.xml"); 16 | } 17 | 18 | function create() { 19 | console.log("create"); 20 | this.i18n.initialize( 21 | { 22 | debug: false, 23 | fallbackLng: "en", 24 | loadPath: "assets/i18n/{{lng}}/{{ns}}.json" 25 | }, 26 | () => { 27 | this.make.text({ x: 0, y: 0, text: "hello" }, true); 28 | this.add.text(60, 0, "world"); 29 | this.add.text(60, 60, "interpolations", null, { 0: "is working" }); 30 | this.add.bitmapText(100, 100, "helvetica_regular", "hello", 50); 31 | this.add.dynamicBitmapText(100, 200, "iceicebaby", "hello", 50); 32 | setTimeout(() => { 33 | this.i18n.changeLanguage("es"); 34 | }, 5000); 35 | } 36 | ); 37 | } 38 | 39 | document.onreadystatechange = () => { 40 | if (document.readyState === "complete") { 41 | const config = { 42 | height: 600, 43 | plugins: { 44 | scene: [ 45 | { 46 | key: "i18nPlugin", 47 | mapping: "i18n", 48 | plugin: I18nPlugin 49 | } 50 | ] 51 | }, 52 | scene: { 53 | create, 54 | preload 55 | }, 56 | type: Phaser.AUTO, 57 | width: 400 58 | }; 59 | (window as any).game = new Game(config); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const path = require("path"); 3 | const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); 4 | 5 | module.exports = env => { 6 | if (!env) env = { production: false, karma: false }; 7 | 8 | let mode = env.production ? "production" : "development"; 9 | let tsconfig = !env.karma ? "tsconfig.json" : "tsconfig.test.json"; 10 | let output = env.production ? "dist" : "dist-test"; 11 | let filename = env.production ? "i18n.min.js" : "i18n.js"; 12 | 13 | return { 14 | mode: mode, 15 | 16 | entry: { 17 | main: path.join(__dirname, "src/index.ts") 18 | }, 19 | 20 | output: { 21 | path: path.join(__dirname, output), 22 | filename: filename, 23 | 24 | libraryTarget: "var", 25 | library: "phaser3i18n" 26 | }, 27 | 28 | devtool: env.production ? undefined : "inline-source-map", 29 | 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.ts$/, 34 | loader: "ts-loader?configFile=" + tsconfig 35 | }, 36 | { 37 | test: env.production /* disable this loader for production builds */ ? /^$/ : /^(.(?!\.test))*\.ts$/, 38 | loader: "istanbul-instrumenter-loader", 39 | query: { 40 | embedSource: true 41 | }, 42 | enforce: "post" 43 | } 44 | ] 45 | }, 46 | 47 | plugins: env.production ? [] : [new webpack.SourceMapDevToolPlugin({ test: /\.ts$/i })], 48 | 49 | optimization: env.production 50 | ? { 51 | concatenateModules: true, 52 | minimize: true, 53 | minimizer: [ 54 | new UglifyJsPlugin({ 55 | cache: true, 56 | parallel: 4, 57 | uglifyOptions: { 58 | output: { 59 | comments: false 60 | } 61 | } 62 | }) 63 | ] 64 | } 65 | : {}, 66 | resolve: { 67 | extensions: [".ts", ".js", ".json"] 68 | } 69 | }; 70 | }; 71 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | process.env.TEST = true; 2 | process.env.NODE_ENV = "test"; 3 | 4 | const webpackConfig = require("./webpack.config.js")({ production: false, karma: true }); 5 | 6 | delete webpackConfig.entry; 7 | 8 | module.exports = config => { 9 | var configuration = { 10 | captureConsole: true, 11 | // proxies: { 12 | // "/static/assets/i18n/en/translation.json": "/assets/i18n/en/translation.json", 13 | // "/static/assets/i18n/es/translation.json": "/assets/i18n/es/translation.json" 14 | // }, 15 | basePath: "", 16 | frameworks: ["mocha", "chai", "sinon", "es6-shim"], 17 | files: [ 18 | { pattern: "node_modules/bluebird/js/browser/bluebird.js", include: true }, 19 | { pattern: "node_modules/phaser/dist/phaser.js", include: true }, 20 | { pattern: "./test/**/**/**.test.ts", include: true }, 21 | { pattern: "**/*.map", served: true, included: false, watched: true }, 22 | { pattern: "assets/**/*.json", watched: false, included: false, served: true } 23 | ], 24 | preprocessors: { 25 | "./**/**/**/**.ts": ["sourcemap"], 26 | "./test/**/**/**.test.ts": ["webpack"] 27 | }, 28 | webpack: webpackConfig, 29 | webpackMiddleware: { 30 | noInfo: true 31 | }, 32 | plugins: [ 33 | "karma-webpack", 34 | "karma-sourcemap-writer", 35 | "karma-sourcemap-loader", 36 | "karma-remap-istanbul", 37 | "karma-mocha-reporter", 38 | "karma-mocha", 39 | "karma-chai", 40 | "karma-sinon", 41 | "karma-es6-shim", 42 | "karma-coverage-istanbul-reporter" 43 | ], 44 | reporters: config.singleRun ? ["dots", "mocha", "coverage-istanbul"] : ["dots", "mocha"], 45 | coverageIstanbulReporter: { 46 | reports: ["html", "lcov", "lcovonly", "text-summary"], 47 | dir: "coverage", 48 | fixWebpackSourcePaths: true, 49 | "report-config": { 50 | html: { 51 | subdir: "html-report" 52 | } 53 | } 54 | }, 55 | port: 9876, 56 | colors: true, 57 | logLevel: config.LOG_INFO, 58 | autoWatch: true, 59 | browsers: [] 60 | }; 61 | 62 | if (process.env.TRAVIS) { 63 | configuration.browsers.push("PhantomJS"); 64 | configuration.plugins.push("karma-phantomjs-launcher"); 65 | } else { 66 | configuration.browsers.push("PhantomJS"); 67 | configuration.plugins.push("karma-phantomjs-launcher"); 68 | } 69 | 70 | config.set(configuration); 71 | }; 72 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@koreez.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /test/com/koreez/plugin/plugin.test.ts: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // Copyright (c) 2018 Koreez LLC. All Rights Reserved. 3 | // 4 | // NOTICE: You are permitted to use, modify, and distribute this file 5 | // in accordance with the terms of the license agreement accompanying it. 6 | // ------------------------------------------------------------------------------ 7 | 8 | import { expect } from "chai"; 9 | import { i18next } from "../../../../src/"; 10 | import { I18nPlugin } from "../../../../src/com/koreez/plugin/I18nPlugin"; 11 | import "../../../entry"; 12 | 13 | describe("i18n Plugin", () => { 14 | it("plugin_is_properly_installing", done => { 15 | const config = { 16 | banner: false, 17 | height: 10, 18 | plugins: { 19 | scene: [ 20 | { 21 | key: "plugin_is_properly_installing", 22 | mapping: "i18n", 23 | plugin: I18nPlugin, 24 | start: true 25 | } 26 | ] 27 | }, 28 | scene: { 29 | create 30 | }, 31 | type: Phaser.AUTO, 32 | width: 10 33 | }; 34 | function create() { 35 | expect(this.i18n).instanceOf(I18nPlugin); 36 | done(); 37 | } 38 | (window as any).game = new Phaser.Game(config); 39 | }); 40 | 41 | it("plugin_is_properly_working", done => { 42 | const config = { 43 | banner: false, 44 | height: 10, 45 | plugins: { 46 | scene: [ 47 | { 48 | key: "plugin_is_properly_working", 49 | mapping: "i18n", 50 | plugin: I18nPlugin 51 | } 52 | ] 53 | }, 54 | scene: { 55 | create 56 | }, 57 | type: Phaser.AUTO, 58 | width: 10 59 | }; 60 | function create() { 61 | this.i18n.initialize( 62 | { 63 | debug: false, 64 | fallbackLng: "en", 65 | loadPath: "base/assets/i18n/{{lng}}/{{ns}}.json" 66 | }, 67 | () => { 68 | const textObject = this.add.text(0, 0, "hello"); 69 | expect(textObject.text).equal("Hello"); 70 | this.i18n.on("languageChanged", () => { 71 | expect(textObject.text).equal("Hola"); 72 | done(); 73 | }); 74 | this.i18n.changeLanguage("es"); 75 | } 76 | ); 77 | } 78 | (window as any).game = new Phaser.Game(config); 79 | }); 80 | 81 | it("plugin_i18next", done => { 82 | const config = { 83 | banner: false, 84 | height: 10, 85 | plugins: { 86 | scene: [ 87 | { 88 | key: "plugin_i18next", 89 | mapping: "i18n", 90 | plugin: I18nPlugin 91 | } 92 | ] 93 | }, 94 | scene: { 95 | create 96 | }, 97 | type: Phaser.AUTO, 98 | width: 10 99 | }; 100 | function create() { 101 | this.i18n.initialize( 102 | { 103 | debug: false, 104 | fallbackLng: "en", 105 | loadPath: "base/assets/i18n/{{lng}}/{{ns}}.json" 106 | }, 107 | () => { 108 | const textObject = this.add.text(0, 0, "hello"); 109 | expect(textObject.text).equal("Hello"); 110 | expect(i18next.t("hello")).equal("Hello"); 111 | this.i18n.on("languageChanged", () => { 112 | expect(textObject.text).equal("Hola"); 113 | expect(i18next.t("hello")).equal("Hola"); 114 | done(); 115 | }); 116 | this.i18n.changeLanguage("es"); 117 | } 118 | ); 119 | } 120 | (window as any).game = new Phaser.Game(config); 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@koreez/phaser3-i18n", 3 | "version": "2.0.6", 4 | "description": "Internationalization for phaser done right. Using the i18next i18n ecosystem.", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "start": "webpack-dev-server --config ./webpack.example.config.js", 9 | "test": "karma start --single-run", 10 | "mocha": "mocha test/**/*.test.ts --require ts-node/register", 11 | "autoformat": "prettier --config .prettierrc --write {src,test}{/,/**/}{*,*.test}.ts", 12 | "tslint-check:src": "tslint-config-prettier-check ./tslint.json", 13 | "tslint-check:test": "tslint-config-prettier-check ./tslint.test.json", 14 | "tslint:src": "tslint --project tsconfig.json", 15 | "tslint:test": "tslint --config tslint.test.json --project tsconfig.test.json", 16 | "tslint": "npm run tslint-check:src && npm run tslint-check:test && npm run tslint:src && npm run tslint:test", 17 | "clean-up": "rimraf .nyc_output && rimraf coverage && rimraf lib && rimraf lib-test && rimraf dist-test", 18 | "compile:src": "tsc -d --importHelpers", 19 | "compile:test": "tsc -p tsconfig.test.json -d --importHelpers", 20 | "dev": "webpack", 21 | "build": "webpack --env.production", 22 | "prepare": "npm run clean-up && npm run compile:src", 23 | "prepublishOnly": "publish-please guard", 24 | "publish-please": "npm run tslint && npm run autoformat && npm run clean-up && npm run test && publish-please" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git@github.com:koreezgames/phaser3-i18n-plugin.git" 29 | }, 30 | "publishConfig": { 31 | "registry": "https://registry.npmjs.org" 32 | }, 33 | "keywords": [ 34 | "Phaser3", 35 | "Phaser", 36 | "internationalization", 37 | "localization", 38 | "i18next", 39 | "i18n" 40 | ], 41 | "author": "Koreez LLC", 42 | "contributors": [ 43 | "Sargis Sargsyan " 44 | ], 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/koreezgames/phaser3-i18n-plugin/issues" 48 | }, 49 | "homepage": "https://github.com/koreezgames/phaser3-i18n-plugin#readme", 50 | "dependencies": { 51 | "i18next": "^15.0.2", 52 | "i18next-xhr-backend": "^2.0.0" 53 | }, 54 | "peerDependencies": { 55 | "phaser": "^3.12.0" 56 | }, 57 | "devDependencies": { 58 | "@types/bluebird": "^3.5.25", 59 | "@types/chai": "^4.1.7", 60 | "@types/i18next": "^12.1.0", 61 | "@types/i18next-xhr-backend": "^1.4.1", 62 | "@types/mocha": "^5.2.1", 63 | "@types/sinon": "^7.0.5", 64 | "bluebird": "^3.5.3", 65 | "browserify-versionify": "^1.0.6", 66 | "chai": "^4.2.0", 67 | "es6-map": "^0.1.5", 68 | "es6-symbol": "^3.1.1", 69 | "glslify": "^7.0.0", 70 | "html-webpack-plugin": "^3.2.0", 71 | "imports-loader": "^0.8.0", 72 | "istanbul": "^0.4.5", 73 | "istanbul-instrumenter-loader": "^3.0.1", 74 | "karma": "^4.0.0", 75 | "karma-chai": "^0.1.0", 76 | "karma-chai-sinon": "^0.1.5", 77 | "karma-chrome-launcher": "^2.2.0", 78 | "karma-coverage-istanbul-reporter": "^2.0.4", 79 | "karma-es6-shim": "^1.0.0", 80 | "karma-mocha": "^1.3.0", 81 | "karma-mocha-reporter": "^2.2.5", 82 | "karma-phantomjs-launcher": "^1.0.4", 83 | "karma-remap-istanbul": "^0.6.0", 84 | "karma-sinon": "^1.0.5", 85 | "karma-sourcemap-loader": "^0.3.7", 86 | "karma-sourcemap-writer": "^0.1.2", 87 | "karma-webpack": "^3.0.5", 88 | "mocha": "^5.2.0", 89 | "phaser": "^3.16.2", 90 | "prettier": "^1.16.4", 91 | "publish-please": "^5.4.3", 92 | "remap-istanbul": "^0.13.0", 93 | "rimraf": "^2.6.3", 94 | "sinon": "^7.2.3", 95 | "sinon-chai": "^3.3.0", 96 | "ts-loader": "^5.3.3", 97 | "ts-node": "^8.0.2", 98 | "tslint": "^5.12.1", 99 | "tslint-config-prettier": "^1.18.0", 100 | "typescript": "^3.3.3", 101 | "uglifyjs-webpack-plugin": "^2.1.1", 102 | "webpack": "^4.29.3", 103 | "webpack-cli": "^3.2.3", 104 | "webpack-concat-plugin": "^3.0.0", 105 | "webpack-dev-server": "^3.1.14" 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phaser3 i18n Plugin 2 | 3 | [![GitHub license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/koreezgames/phaser3-i18n-plugin/blob/master/LICENSE) 4 | [![Build Status](https://secure.travis-ci.org/koreezgames/phaser3-i18n-plugin.svg?branch=master)](https://travis-ci.org/koreezgames/phaser3-i18n-plugin) 5 | [![codebeat badge](https://codebeat.co/badges/753f25dc-b8b8-4b55-9559-2bdc00b070e1)](https://codebeat.co/projects/github-com-koreezgames-phaser-i18next-master) 6 | [![Test Coverage](https://api.codeclimate.com/v1/badges/63e0c72189fa97ca55db/test_coverage)](https://codeclimate.com/github/koreezgames/phaser3-i18n-plugin/test_coverage) 7 | [![npm version](https://badge.fury.io/js/%40koreez%2Fphaser3-i18n.svg)](https://badge.fury.io/js/%40koreez%2Fphaser3-i18n) 8 | [![Greenkeeper badge](https://badges.greenkeeper.io/koreezgames/phaser3-i18n-plugin.svg)](https://greenkeeper.io/) 9 | [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 10 | 11 | Phaser3 i18n is a plugin for Phaser 3 that allows you to have seamless translations in your game. It uses **[i18next](https://github.com/i18next/i18next)** as it's source for translations management, which is widely adopted by the JS community in other projects as well. 12 | 13 | Key features: 14 | 15 | - Support for translations namespaces 16 | - Simple key/value JSON 17 | - Seamless switching of languages 18 | - No extra function calls for translating strings, directly build into Phaser's Text object 19 | 20 | ## Getting Started 21 | 22 | ### Installation 23 | 24 | [![npm](https://img.shields.io/npm/dt/@koreez/phaser3-i18n.svg)](https://www.npmjs.com/package/@koreez/phaser3-i18n) 25 | 26 | ```shell 27 | $ npm i -g npm 28 | $ npm i --save @koreez/phaser3-i18n 29 | ``` 30 | 31 | ## Usage 32 | 33 | ### Import the plugin 34 | 35 | ```javascript 36 | import { I18nPlugin } from "@koreez/phaser3-i18n"; 37 | ``` 38 | 39 | ### Load the plugin 40 | 41 | You need to load the plugin in your game. This is done just like any other plugin in Phaser 3. 42 | So, to load the plugin, include it in plugins config. 43 | 44 | ```javascript 45 | const config = { 46 | type: Phaser.AUTO, 47 | parent: "phaser-example", 48 | width: 800, 49 | height: 600, 50 | plugins: { 51 | scene: [ 52 | { 53 | key: "i18nPlugin", 54 | plugin: I18nPlugin, 55 | mapping: "i18n" 56 | } 57 | ] 58 | }, 59 | scene: { 60 | create: create 61 | } 62 | }; 63 | ``` 64 | 65 | ### Initialize the plugin 66 | 67 | After plugin has been loaded you need to initialize it. 68 | 69 | ```javascript 70 | create () { 71 | this.i18n.initialize( 72 | { 73 | fallbackLng: 'en', 74 | loadPath: 'assets/i18n/{{lng}}/{{ns}}.json', 75 | debug: false, 76 | }, 77 | function () { 78 | console.log('I18nPlugin initialized!') 79 | }, 80 | ) 81 | } 82 | ``` 83 | 84 | The plugin will patch _add.text, add.bitmapText and add.dynamicBitmapText / make.text, make.bitmapText and make.dynamicBitmapText_ methods with additional functionality. 85 | 86 | ### Create localized texts 87 | 88 | ```javascript 89 | // x - any 90 | // y - any 91 | // font - bitmap font 92 | // text - should be translation key 93 | // size - font size 94 | // interpolations - interpolation for translation (for example { 0: "value0", 1: "value1" }), note this is not required parameter 95 | this.add.bitmapText(x, y, font, text, size, interpolations); 96 | ``` 97 | 98 | ##### **_or via config_** 99 | 100 | ```javascript 101 | var config = { 102 | x: 100, 103 | y: 100, 104 | text: "translationKey", 105 | font: "atari-classic", 106 | size: 64, 107 | interpolations: { 0: "value0", 1: "value1" } 108 | }; 109 | this.make.dynamicBitmapText(config); 110 | ``` 111 | 112 | ## Manage translations 113 | 114 | When you're all done and set up for translations and have your key value file, next up is starting to translate them. For smaller games this is fairly simple and still manageable by editing your translations in a text file. 115 | But when you start with bigger games and more translations (and translation namespaces) you want to manage your translations better, and maybe even use external translator services. 116 | 117 | Two of those service you could use are [locize](https://locize.com/) and/or [Poeditor](https://poeditor.com). Both these services allow you to online edit key value JSON translations that are used in i18next. 118 | The advantage of using such tools is that it's easier to allow external people work on your translations and they aggregate the statistics of your projects (translation completion, languages managed, etc.) 119 | 120 | ## Credits 121 | 122 | Big thanks to this great repo: 123 | 124 | https://github.com/orange-games/phaser-i18next 125 | 126 | ## License 127 | 128 | [MIT](LICENSE) 129 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/i18n/textExtensions.ts: -------------------------------------------------------------------------------- 1 | import i18next from "i18next"; 2 | 3 | const setText: (value: string) => any = function(value: string): any { 4 | if (value !== this._i18nKey) { 5 | this._i18nKey = value.toString() || ""; 6 | } 7 | return this._setText(i18next.t(this._i18nKey, this._interpolations) || ""); 8 | }; 9 | 10 | const interpolations: any = { 11 | get(): any { 12 | return this._interpolations; 13 | }, 14 | 15 | set(value: any): void { 16 | this._interpolations = value; 17 | this.setText(this._i18nKey); 18 | } 19 | }; 20 | 21 | const setTranslationParameter: (key: string, value: any) => void = function(key: string, value: any): void { 22 | if (!this._interpolations) { 23 | this._interpolations = {}; 24 | } 25 | this._interpolations[key] = value; 26 | this.setText(this._i18nKey); 27 | }; 28 | 29 | const clearTranslationParameter: (key: string) => void = function(key: string): void { 30 | if (key in this._interpolations) { 31 | delete this._interpolations[key]; 32 | } 33 | this.setText(this._i18nKey); 34 | }; 35 | 36 | const commonExtend: (clazz: any, prop: string) => void = (clazz: any, prop: string): void => { 37 | if (clazz.prototype.setText !== setText) { 38 | clazz.prototype._setText = clazz.prototype.setText; 39 | 40 | clazz.prototype.setText = setText; 41 | 42 | Object.defineProperty(clazz.prototype, "interpolations", interpolations); 43 | 44 | clazz.prototype.setTranslationParameter = setTranslationParameter; 45 | 46 | clazz.prototype.clearTranslationParameter = clearTranslationParameter; 47 | } 48 | 49 | const creator: any = Phaser.GameObjects.GameObjectCreator; 50 | if (creator) { 51 | const textCreator: string = creator.prototype[prop]; 52 | if (textCreator) { 53 | delete creator.prototype[prop]; 54 | creator.register(`_${prop}`, textCreator); 55 | creator.register(prop, function(config: any, addToScene: boolean = false): Phaser.GameObjects.GameObject { 56 | const _text: Phaser.GameObjects.GameObject = this.scene.make[`_${prop}`](config, addToScene); 57 | (_text as any).interpolations = config.interpolations; 58 | return _text; 59 | }); 60 | } 61 | } 62 | 63 | const factory: any = Phaser.GameObjects.GameObjectFactory; 64 | if (factory) { 65 | const textFactory: string = factory.prototype[prop]; 66 | if (textFactory) { 67 | delete factory.prototype[prop]; 68 | factory.register(`_${prop}`, textFactory); 69 | } 70 | } 71 | }; 72 | 73 | const textExtensions: any = { 74 | extendText: () => { 75 | const text: any = Phaser.GameObjects.Text; 76 | if (text) { 77 | commonExtend(text, "text"); 78 | } 79 | const gameObjectFactory: any = Phaser.GameObjects.GameObjectFactory; 80 | if (!gameObjectFactory) { 81 | return; 82 | } 83 | gameObjectFactory.register("text", function( 84 | x: any, 85 | y: any, 86 | str: any, 87 | style: any, 88 | theInterpolations: any 89 | ): Phaser.GameObjects.GameObject { 90 | const aText: Phaser.GameObjects.GameObject = this.scene.add._text(x, y, str, style); 91 | (aText as any).interpolations = theInterpolations; 92 | return aText; 93 | }); 94 | }, 95 | 96 | extendBitmapText: () => { 97 | const bitmapText: any = Phaser.GameObjects.BitmapText; 98 | if (bitmapText) { 99 | commonExtend(bitmapText, "bitmapText"); 100 | } 101 | const gameObjectFactory: any = Phaser.GameObjects.GameObjectFactory; 102 | if (!gameObjectFactory) { 103 | return; 104 | } 105 | gameObjectFactory.register("bitmapText", function( 106 | x: any, 107 | y: any, 108 | font: any, 109 | str: any, 110 | size: any, 111 | theInterpolations: any 112 | ): Phaser.GameObjects.GameObject { 113 | const aText: Phaser.GameObjects.GameObject = this.scene.add._bitmapText(x, y, font, str, size); 114 | (aText as any).interpolations = theInterpolations; 115 | return aText; 116 | }); 117 | }, 118 | 119 | extendDynamicBitmapText: () => { 120 | const dynamicBitmapText: any = Phaser.GameObjects.DynamicBitmapText; 121 | if (dynamicBitmapText) { 122 | commonExtend(dynamicBitmapText, "dynamicBitmapText"); 123 | } 124 | const gameObjectFactory: any = Phaser.GameObjects.GameObjectFactory; 125 | if (!gameObjectFactory) { 126 | return; 127 | } 128 | gameObjectFactory.register("dynamicBitmapText", function( 129 | x: any, 130 | y: any, 131 | font: any, 132 | str: any, 133 | size: any, 134 | theInterpolations: any 135 | ): Phaser.GameObjects.GameObject { 136 | const aText: Phaser.GameObjects.GameObject = this.scene.add._dynamicBitmapText(x, y, font, str, size); 137 | (aText as any).interpolations = theInterpolations; 138 | return aText; 139 | }); 140 | } 141 | }; 142 | 143 | export default textExtensions; 144 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/i18n/Ii18n.ts: -------------------------------------------------------------------------------- 1 | import i18next from "i18next"; 2 | 3 | export interface Ii18n { 4 | /** 5 | * List of modules used 6 | */ 7 | modules: i18next.Modules; 8 | 9 | /** 10 | * Internal container for all used plugins and implementation details like languageUtils, pluralResolvers, etc. 11 | */ 12 | services: i18next.Services; 13 | 14 | /** 15 | * Uses similar args as the t function and returns true if a key exists. 16 | */ 17 | exists: i18next.ExistsFunction; 18 | 19 | /** 20 | * Is set to the current detected or set language. 21 | * If you need the primary used language depending on your configuration (whilelist, load) you will prefer using i18next.languages[0]. 22 | */ 23 | language: string; 24 | 25 | /** 26 | * Is set to an array of language-codes that will be used it order to lookup the translation value. 27 | */ 28 | languages: string[]; 29 | 30 | /** 31 | * Exposes interpolation.format function added on init. 32 | */ 33 | format: i18next.FormatFunction; 34 | 35 | /** 36 | * Current options 37 | */ 38 | options: i18next.InitOptions; 39 | 40 | /** 41 | * Is initialized 42 | */ 43 | isInitialized: boolean; 44 | 45 | loadResources(callback?: (err: any) => void): void; 46 | 47 | /** 48 | * Returns a t function that defaults to given language or namespace. 49 | * Both params could be arrays of languages or namespaces and will be treated as fallbacks in that case. 50 | * On the returned function you can like in the t function override the languages or namespaces by passing them in options or by prepending namespace. 51 | */ 52 | getFixedT(lng: string | string[], ns?: string | string[]): i18next.TFunction; 53 | getFixedT(lng: null, ns: string | string[]): i18next.TFunction; 54 | 55 | /** 56 | * Changes the language. The callback will be called as soon translations were loaded or an error occurs while loading. 57 | * HINT: For easy testing - setting lng to 'cimode' will set t function to always return the key. 58 | */ 59 | changeLanguage(lng: string, callback?: i18next.Callback): Promise; 60 | 61 | /** 62 | * Loads additional namespaces not defined in init options. 63 | */ 64 | loadNamespaces(ns: string | string[], callback?: i18next.Callback): Promise; 65 | 66 | /** 67 | * Loads additional languages not defined in init options (preload). 68 | */ 69 | loadLanguages(lngs: string | string[], callback?: i18next.Callback): Promise; 70 | 71 | /** 72 | * Reloads resources on given state. Optionally you can pass an array of languages and namespaces as params if you don't want to reload all. 73 | */ 74 | reloadResources(lngs?: string | string[], ns?: string | string[], callback?: () => void): Promise; 75 | reloadResources(lngs: null, ns: string | string[], callback?: () => void): Promise; 76 | 77 | /** 78 | * Changes the default namespace. 79 | */ 80 | setDefaultNamespace(ns: string): void; 81 | 82 | /** 83 | * Returns rtl or ltr depending on languages read direction. 84 | */ 85 | dir(lng?: string): "ltr" | "rtl"; 86 | 87 | /** 88 | * Will return a new i18next instance. 89 | * Please read the options page for details on configuration options. 90 | * Providing a callback will automatically call init. 91 | * The callback will be called after all translations were loaded or with an error when failed (in case of using a backend). 92 | */ 93 | createInstance(options?: i18next.InitOptions, callback?: i18next.Callback): i18next.i18n; 94 | 95 | /** 96 | * Creates a clone of the current instance. Shares store, plugins and initial configuration. 97 | * Can be used to create an instance sharing storage but being independent on set language or namespaces. 98 | */ 99 | cloneInstance(options?: i18next.InitOptions, callback?: i18next.Callback): i18next.i18n; 100 | 101 | /** 102 | * Gets fired after initialization. 103 | */ 104 | on(event: "initialized", callback: (options: i18next.InitOptions) => void): void; 105 | 106 | /** 107 | * Gets fired on loaded resources. 108 | */ 109 | on(event: "loaded", callback: (loaded: boolean) => void): void; 110 | 111 | /** 112 | * Gets fired if loading resources failed. 113 | */ 114 | on(event: "failedLoading", callback: (lng: string, ns: string, msg: string) => void): void; 115 | 116 | /** 117 | * Gets fired on accessing a key not existing. 118 | */ 119 | on(event: "missingKey", callback: (lngs: string[], namespace: string, key: string, res: string) => void): void; 120 | 121 | /** 122 | * Gets fired when resources got added or removed. 123 | */ 124 | on(event: "added" | "removed", callback: (lng: string, ns: string) => void): void; 125 | 126 | /** 127 | * Gets fired when changeLanguage got called. 128 | */ 129 | on(event: "languageChanged", callback: (lng: string) => void): void; 130 | 131 | /** 132 | * Event listener 133 | */ 134 | on(event: string, listener: (...args: any[]) => void): void; 135 | 136 | /** 137 | * Remove event listener 138 | */ 139 | off(event: string, listener: (...args: any[]) => void): void; 140 | 141 | /** 142 | * Gets one value by given key. 143 | */ 144 | getResource(lng: string, ns: string, key: string, options?: { keySeparator?: string }): any; 145 | 146 | /** 147 | * Adds one key/value. 148 | */ 149 | addResource(lng: string, ns: string, key: string, value: string, options?: { keySeparator?: string; silent?: boolean }): void; 150 | 151 | /** 152 | * Adds multiple key/values. 153 | */ 154 | addResources(lng: string, ns: string, resources: any): void; 155 | 156 | /** 157 | * Adds a complete bundle. 158 | * Setting deep param to true will extend existing translations in that file. 159 | * Setting overwrite to true it will overwrite existing translations in that file. 160 | */ 161 | addResourceBundle(lng: string, ns: string, resources: any, deep?: boolean, overwrite?: boolean): void; 162 | 163 | /** 164 | * Checks if a resource bundle exists. 165 | */ 166 | hasResourceBundle(lng: string, ns: string): boolean; 167 | 168 | /** 169 | * Returns a resource bundle. 170 | */ 171 | getResourceBundle(lng: string, ns: string): any; 172 | 173 | /** 174 | * Removes an existing bundle. 175 | */ 176 | removeResourceBundle(lng: string, ns: string): void; 177 | } 178 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/I18nPlugin.ts: -------------------------------------------------------------------------------- 1 | import i18next from "i18next"; 2 | import * as XHR from "i18next-xhr-backend"; 3 | import { Ii18n } from "./i18n/Ii18n"; 4 | import textExtensions from "./i18n/textExtensions"; 5 | 6 | export class I18nPlugin extends Phaser.Plugins.ScenePlugin implements Ii18n { 7 | public static staticConstructor(): any { 8 | textExtensions.extendText(); 9 | 10 | textExtensions.extendBitmapText(); 11 | 12 | textExtensions.extendDynamicBitmapText(); 13 | } 14 | 15 | public get modules(): i18next.Modules { 16 | return i18next.modules; 17 | } 18 | 19 | public get services(): i18next.Services { 20 | return i18next.services; 21 | } 22 | 23 | private languageChangedBound: any; 24 | 25 | // Called when the Plugin is booted by the PluginManager. 26 | // If you need to reference other systems in the Scene (like the Loader or DisplayList) then set-up those references now, not in the constructor. 27 | public boot(): void { 28 | const eventEmitter: Phaser.Events.EventEmitter = this.systems.events; 29 | 30 | // Listening to the following events is entirely optional, although we would recommend cleanly shutting down and destroying at least. 31 | // If you don't need any of these events then remove the listeners and the relevant methods too. 32 | 33 | eventEmitter.on("shutdown", this.shutdown, this); 34 | eventEmitter.on("shutdown", this.shutdown, this); 35 | this.languageChangedBound = this.languageChanged.bind(this); 36 | this.on("languageChanged", this.languageChangedBound); 37 | } 38 | 39 | /** 40 | * @param options - Initial options. 41 | * @param callback - will be called after all translations were loaded or with an error when failed (in case of using a backend). 42 | */ 43 | public initialize(options: any, callback?: i18next.Callback): void { 44 | i18next.use(new XHR(null, options)); 45 | if (options) { 46 | i18next.init(options, callback); 47 | return; 48 | } 49 | i18next.init(callback); 50 | } 51 | 52 | /** 53 | * The use function is there to load additional plugins to i18next. 54 | * For available module see the plugins page and don't forget to read the documentation of the plugin. 55 | */ 56 | public use(module: any): i18next.i18n { 57 | return i18next.use(module); 58 | } 59 | 60 | public t< 61 | TResult extends string | object | Array | undefined = string, 62 | TKeys extends string = string, 63 | TValues extends object = object 64 | >(key: TKeys | TKeys[], options?: i18next.TOptions): TResult { 65 | return i18next.t(key, options); 66 | } 67 | 68 | public exists(key: string | string[], options?: i18next.InterpolationOptions): boolean { 69 | return i18next.exists(key, options); 70 | } 71 | 72 | public loadResources(callback?: (err: any) => void): void { 73 | i18next.loadResources(callback); 74 | } 75 | 76 | public createInstance(options?: i18next.InitOptions, callback?: i18next.Callback): i18next.i18n { 77 | return i18next.createInstance(options, callback); 78 | } 79 | 80 | public cloneInstance(options?: i18next.InitOptions, callback?: i18next.Callback): i18next.i18n { 81 | return i18next.cloneInstance(options, callback); 82 | } 83 | 84 | /** 85 | * Returns a t function that defaults to given language or namespace. 86 | * Both params could be arrays of languages or namespaces and will be treated as fallbacks in that case. 87 | * On the returned function you can like in the t function override the languages or namespaces by passing them in options or by prepending namespace. 88 | */ 89 | public getFixedT(lng: string | string[], ns?: string | string[]): i18next.TFunction { 90 | return i18next.getFixedT(lng, ns); 91 | } 92 | 93 | /** 94 | * Changes the language. The callback will be called as soon translations were loaded or an error occurs while loading. 95 | * HINT: For easy testing - setting lng to 'cimode' will set t function to always return the key. 96 | */ 97 | public changeLanguage(lng: string, callback?: i18next.Callback): Promise { 98 | return i18next.changeLanguage(lng, callback); 99 | } 100 | 101 | /** 102 | * Is set to the current detected or set language. 103 | * If you need the primary used language depending on your configuration (whilelist, load) you will prefer using i18next.languages[0]. 104 | */ 105 | public get language(): string { 106 | return i18next.language; 107 | } 108 | 109 | /** 110 | * Is set to an array of language-codes that will be used it order to lookup the translation value. 111 | */ 112 | public get languages(): string[] { 113 | return i18next.languages; 114 | } 115 | 116 | /** 117 | * Loads additional namespaces not defined in init options. 118 | */ 119 | public loadNamespaces(ns: string | string[], callback: i18next.Callback): Promise { 120 | return i18next.loadNamespaces(ns, callback); 121 | } 122 | 123 | /** 124 | * Loads additional languages not defined in init options (preload). 125 | */ 126 | public loadLanguages(lngs: string | string[], callback: i18next.Callback): Promise { 127 | return i18next.loadLanguages(lngs, callback); 128 | } 129 | 130 | /** 131 | * Reloads resources on given state. Optionally you can pass an array of languages and namespaces as params if you don't want to reload all. 132 | */ 133 | public reloadResources(lngs?: string[], ns?: string[]): Promise { 134 | return i18next.reloadResources(lngs, ns); 135 | } 136 | 137 | /** 138 | * Changes the default namespace. 139 | */ 140 | public setDefaultNamespace(ns: string): void { 141 | i18next.setDefaultNamespace(ns); 142 | } 143 | 144 | /** 145 | * Returns rtl or ltr depending on languages read direction. 146 | */ 147 | public dir(lng?: string): "ltr" | "rtl" { 148 | return i18next.dir(lng); 149 | } 150 | 151 | /** 152 | * Exposes interpolation.format function added on init. 153 | */ 154 | public get format(): i18next.FormatFunction { 155 | return i18next.format; 156 | } 157 | 158 | /** 159 | * Event listener 160 | */ 161 | public on(event: string, listener: (...args: any[]) => void): void { 162 | i18next.on(event, listener); 163 | } 164 | 165 | /** 166 | * Remove event listener 167 | */ 168 | public off(event: string, listener: (...args: any[]) => void): void { 169 | i18next.off(event, listener); 170 | } 171 | 172 | /** 173 | * Gets one value by given key. 174 | */ 175 | public getResource( 176 | lng: string, 177 | ns: string, 178 | key: string, 179 | options?: { 180 | keySeparator?: string; 181 | } 182 | ): any { 183 | i18next.getResource(lng, ns, key, options); 184 | } 185 | 186 | /** 187 | * Adds one key/value. 188 | */ 189 | public addResource( 190 | lng: string, 191 | ns: string, 192 | key: string, 193 | value: string, 194 | options?: { 195 | keySeparator?: string; 196 | silent?: boolean; 197 | } 198 | ): void { 199 | i18next.addResource(lng, ns, key, value, options); 200 | } 201 | 202 | /** 203 | * Adds multiple key/values. 204 | */ 205 | public addResources(lng: string, ns: string, resources: any): void { 206 | i18next.addResources(lng, ns, resources); 207 | } 208 | 209 | /** 210 | * Adds a complete bundle. 211 | * Setting deep param to true will extend existing translations in that file. 212 | * Setting overwrite to true it will overwrite existing translations in that file. 213 | */ 214 | public addResourceBundle(lng: string, ns: string, resources: any, deep?: boolean, overwrite?: boolean): void { 215 | i18next.addResourceBundle(lng, ns, resources, deep, overwrite); 216 | } 217 | 218 | /** 219 | * Checks if a resource bundle exists. 220 | */ 221 | public hasResourceBundle(lng: string, ns: string): boolean { 222 | return i18next.hasResourceBundle(lng, ns); 223 | } 224 | 225 | /** 226 | * Returns a resource bundle. 227 | */ 228 | public getResourceBundle(lng: string, ns: string): any { 229 | return i18next.getResourceBundle(lng, ns); 230 | } 231 | 232 | /** 233 | * Removes an existing bundle. 234 | */ 235 | public removeResourceBundle(lng: string, ns: string): void { 236 | i18next.removeResourceBundle(lng, ns); 237 | } 238 | 239 | /** 240 | * Current options 241 | */ 242 | public get options(): i18next.InitOptions { 243 | return i18next.options; 244 | } 245 | 246 | /** 247 | * Is initialized 248 | */ 249 | public get isInitialized(): boolean { 250 | return i18next.isInitialized; 251 | } 252 | 253 | public recursiveUpdateText(obj: any): void { 254 | if ( 255 | obj instanceof Phaser.GameObjects.Text || 256 | obj instanceof Phaser.GameObjects.BitmapText || 257 | obj instanceof Phaser.GameObjects.DynamicBitmapText 258 | ) { 259 | (obj as any).setText((obj as any)._i18nKey); 260 | return; 261 | } 262 | if (obj instanceof Phaser.GameObjects.Container) { 263 | obj.list.forEach((child: any) => { 264 | this.recursiveUpdateText(child); 265 | }); 266 | return; 267 | } 268 | 269 | if (obj.children && obj.children.length > 0) { 270 | obj.children.each((child: any) => { 271 | this.recursiveUpdateText(child); 272 | }); 273 | } 274 | } 275 | 276 | private shutdown(): void { 277 | this.off("languageChanged", this.languageChangedBound); 278 | const eventEmitter: Phaser.Events.EventEmitter = this.systems.events; 279 | eventEmitter.off("shutdown", this.shutdown, this, false); 280 | eventEmitter.off("shutdown", this.shutdown, this, false); 281 | this.scene = null; 282 | } 283 | 284 | private languageChanged(): void { 285 | this.recursiveUpdateText(this.scene); 286 | } 287 | } 288 | 289 | I18nPlugin.staticConstructor(); 290 | -------------------------------------------------------------------------------- /assets/bitmapfonts/iceicebaby.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /assets/bitmapfonts/helvetica_regular.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /static/assets/bitmapfonts/helvetica_regular.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | --------------------------------------------------------------------------------