├── .gitattributes
├── .github
└── FUNDING.yml
├── .gitignore
├── index.html
├── languages.json
├── multilang.js
├── readme.md
└── style.css
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
19 | $ cat .gitattributes
20 | index.html linguist-documentation
21 | style.css linguist-documentation
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: ['https://www.buymeacoffee.com/bdr76']
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear in the root of a volume
35 | .DocumentRevisions-V100
36 | .fseventsd
37 | .Spotlight-V100
38 | .TemporaryItems
39 | .Trashes
40 | .VolumeIcon.icns
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JavaScript multi-language localisation example
6 |
7 |
8 |
9 |
67 |
68 | Select language from list to switch between translations:
69 |
71 |
72 |
73 |
Start game
74 |
Sound
75 |
Editor
76 |
Music
77 |
Options
78 |
79 |
84 |
92 | End text
93 |
94 |
95 |
--------------------------------------------------------------------------------
/languages.json:
--------------------------------------------------------------------------------
1 | {
2 | "en": {
3 | "langdesc": "English",
4 | "Start": "Start game",
5 | "Editor": "Editor",
6 | "Options": "Options",
7 | "Sound": "Sound",
8 | "Music": "Music",
9 | "Reset game": "This will reset your game progress. Are you sure?",
10 | "Yes": "Yes",
11 | "No": "No",
12 | "Credits1": "Program by",
13 | "Credits2": "Artwork and graphics by",
14 | "Credits3": "Special thanks to",
15 | "EndText": "Congratulations! Thank you for playing!"
16 | },
17 | "de": {
18 | "langdesc": "German",
19 | "Start": "Spiel starten",
20 | "Editor": "Editor",
21 | "Options": "Optionen",
22 | "Sound": "Geräusche",
23 | "Music": "Musik",
24 | "Reset game": "Möchten Sie Ihren Fortschritt wirklich zurücksetzen?",
25 | "Yes": "Ja",
26 | "No": "Nein",
27 | "Credits1": "Programm von",
28 | "Credits2": "Artwork und Grafiken von",
29 | "Credits3": "Mit Dank an",
30 | "EndText": "Gratuliere! Danke für's Spielen!"
31 | },
32 | "fr": {
33 | "langdesc": "French",
34 | "Start": "Jouer",
35 | "Editor": "Éditer",
36 | "Options": "Paramètres",
37 | "Sound": "Sons",
38 | "Music": "Musique",
39 | "Reset game": "Voulez-vous vraiment réinitialiser votre progression?",
40 | "Yes": "Oui",
41 | "No": "Non",
42 | "Credits1": "Programme de",
43 | "Credits2": "Illustrations et graphiques de",
44 | "Credits3": "Remerciements à",
45 | "EndText": "Félicitations! Merci d'avoir joué le game!"
46 | },
47 | "es": {
48 | "langdesc": "Spanish",
49 | "Start": "Comenzar",
50 | "Editor": "Editor",
51 | "Options": "Opciones",
52 | "Sound": "Sonido",
53 | "Music": "Música",
54 | "Reset game": "Reiniciar todos los puzzles resueltos. ¿Estás seguro?",
55 | "Yes": "Sí",
56 | "No": "No",
57 | "Credits1": "Programa por",
58 | "Credits2": "Ilustraciones y gráficos por",
59 | "Credits3": "Con agradecimiento",
60 | "EndText": "¡Felicidades! ¡Gracias por jugar!"
61 | },
62 | "nl": {
63 | "langdesc": "Dutch",
64 | "Start": "Start spel",
65 | "Editor": "Bewerken",
66 | "Options": "Opties",
67 | "Sound": "Geluid",
68 | "Music": "Muziek",
69 | "Reset game": "Hiermee wis je alle spelvoortgang. Weet je het zeker?",
70 | "Yes": "Ja",
71 | "No": "Nee",
72 | "Credits1": "Programma door",
73 | "Credits2": "Tekeningen en illustraties door",
74 | "Credits3": "Met dank aan",
75 | "EndText": "Gefeliciteerd! Bedankt voor het spelen!"
76 | },
77 | "ru": {
78 | "langdesc": "Russian",
79 | "Start": "Начать игру",
80 | "Editor": "Редактор",
81 | "Options": "Опции",
82 | "Sound": "звук",
83 | "Music": "Музыка",
84 | "Reset game": "Это приведет к сбросу ваш прогресс. Ты уверен?",
85 | "Yes": "да",
86 | "No": "Нет",
87 | "Credits1": "Запрограммированный",
88 | "Credits2": "Иллюстрации",
89 | "Credits3": "Особая благодарность",
90 | "EndText": "Поздравления! Спасибо за игру!"
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/multilang.js:
--------------------------------------------------------------------------------
1 | // MultiLang - BdR 2016
2 | // JavaScript object to handle multilanguage, load phrases from JSON etc.
3 |
4 | var MultiLang = function(url, lang, onload)
5 | {
6 | // variables
7 | this.phrases = {};
8 |
9 | // language code from parameter or if null then default to browser language preference
10 | // Keep only first two characters, for example 'en-US' -> 'en', or 'nl-NL' -> 'nl' etc.
11 | this.selectedLanguage = (lang || navigator.language || navigator.userLanguage).substring(0, 2);
12 |
13 | // onLoad callback function, call after loading JSON
14 | this.onLoad = onload;
15 |
16 | // load json from url
17 | if (typeof url !== 'undefined') {
18 | var obj = this;
19 | var req = new XMLHttpRequest();
20 |
21 | // NOTE: will load asynchronously!
22 | req.open("GET", url, true);
23 | //req.setRequestHeader("User-Agent", navigator.userAgent);
24 | req.onreadystatechange = function (evt) {
25 | if (evt.target.readyState == 4 && evt.target.status == 200) // status == 200, do not allow "Cross origin requests"
26 | //if (evt.target.readyState == 4)// TESTING allow "Cross origin requests" to load from local harddisk
27 | {
28 | // load translations
29 | this.phrases = JSON.parse( evt.target.responseText );
30 |
31 | // verify that the currently selected language exists in the translations
32 | this.setLanguage(this.selectedLanguage);
33 |
34 | // do callback when loading JSON is ready
35 | if (this.onLoad) {
36 | this.onLoad();
37 | }
38 |
39 | };
40 | }.bind(obj); // NOTE: bind onreadyfunction to MultiLang instead of XMLHttpRequest, so MultiLang.phrases will be set instead of added to XMLHttpRequest
41 | req.addEventListener("error", function(e) {
42 | console.log('MultiLang.js: Error reading json file.');
43 | }, false);
44 |
45 | req.send(null);
46 | };
47 |
48 | this.setLanguage = function(langcode) {
49 |
50 | // check if language code does not exist in available translations in json file
51 | // For example, available translated texts in json are 'en' and 'fr', but client language is 'es'
52 | if (!this.phrases.hasOwnProperty(langcode)) {
53 | // doesn't exist so default to the first available language, i.e. the top-most language in json file
54 |
55 | // NOTE: the order of properties in a JSON object are not *guaranteed* to be the same as loading time,
56 | // however in practice all browsers do return them in order
57 | for (var key in this.phrases) {
58 | if (this.phrases.hasOwnProperty(key)) {
59 | langcode = key; // take the first language code
60 | break;
61 | };
62 | };
63 | };
64 |
65 | // set as selected language code
66 | this.selectedLanguage = langcode;
67 | };
68 |
69 | this.get = function(key) {
70 | // get key phrase
71 | var str;
72 |
73 | // check if any languages were loaded
74 | if (this.phrases[this.selectedLanguage]) str = this.phrases[this.selectedLanguage][key];
75 |
76 | // if key does not exist, return the literal key
77 | str = (str || key);
78 |
79 | return str;
80 | };
81 | }
82 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | MultiLang.js
2 | ============
3 |
4 | MultiLang.js is a JavaScript object to handle multilanguage, localisation, JSON file loading, detect default language, detect non-existing phrases etc.
5 |
6 | It contains a working example page and a JSON example file with multiple languages. Initially created for Phaser.js games, but it can be applied to any JavaScript project or website.
7 |
8 | How to use
9 | ----------
10 | Store two or more translations in a JSON file, for example `languages.json` like this:
11 |
12 | {
13 | "en": {
14 | "Hello": "Hello World!",
15 | "SaveChanges": "Do you want to save the changes?",
16 | "Yes": "Yes",
17 | "No": "No"
18 | },
19 | "fr": {
20 | "Hello": "Bonjour à tous!",
21 | "SaveChanges": "Voulez-vous sauvegarder les modifications?",
22 | "Yes": "Oui",
23 | "No": "Non"
24 | },
25 | "zh": {
26 | "Hello": "大家好",
27 | "SaveChanges": "是否要保存更改?",
28 | "Yes": "要",
29 | "No": "不要"
30 | }
31 | }
32 |
33 | And then MultiLang.js can be used like this:
34 |
35 | var multilang = new MultiLang('languages.json');
36 | multilang.setLanguage('fr');
37 | alert( multilang.get('Hello') );
38 |
39 | Manage translations
40 | -------------------
41 | Keeping track of all the translated strings can be a challenge, even for smaller projects. See this [Excel spreadsheet](https://github.com/BdR76/Manage-translations) to manage translations more easily. The translator(s) can fill the spreadsheet with translated texts and the JSON file can then be generated using a Excel VBA or LibreOffice macro. For more information [see here](http://stackoverflow.com/questions/20324967/localize-multi-plattform-projects-consolidate-string-files/20330497#20330497).
42 |
43 | Also see the [Manage translations](https://github.com/BdR76/Manage-translations) github page
44 |
45 | spreadsheet --> macro --> JSON
46 | XML strings
47 | String.localization
48 | resx
49 |
50 | History
51 | -------
52 | 2016-oct-28 upload project on github
53 |
54 | Send any questions or comments to Bas de Reuver - bdr1976@gmail.com
55 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | /* ===== GENERAL ===== */
2 | body, html {
3 | font-family:'Gotham Rounded A', 'Gotham Rounded B', sans-serif;
4 | font-size: 24px;
5 | }
6 | div {
7 | background-color: #E0E0E0;
8 | padding: 10px 10px 10px 10px;
9 | margin: 10px 10px 10px 10px;
10 | width: 500px;
11 | display: inline-block;
12 | }
13 |
14 | div.gr {
15 | background-color: #4CAF50;
16 | color: #fff;
17 | padding: 10px;
18 | float: left;
19 | width: 100px;
20 | }
21 |
22 | div.rd {
23 | color: #fff;
24 | float: left;
25 | background-color: #AF4C50;
26 | padding: 10px;
27 | width: 100px;
28 | }
29 |
30 | div.bl {
31 | color: #fff;
32 | background-color: #4C50AF;
33 | padding: 10px;
34 | width: 200px;
35 | }
36 |
--------------------------------------------------------------------------------