Streamline javascript translation by improving translationRunner (#2808)
* package.json: Add "build:*" targets * Improve react-intl-translations-manager workflow. * Added "build:production" to build production bundle. * Added "build:development" to build development bundle. * Fix json translation files * Run `yarn manage:translations` to fix translation files. * Fix `pl.json` for syntax error. * translationRunner: auto detect existing languages * Auto detect existing rfc5646 language tag in *.json filenames in `app/javascript/mastodon/locale` folder. No need to manually define every new language in the languages array here. * translationRunner: add more functionality * Allow script user to specify language code to check. * Added available language check. * Added --force flag to force creation of unexists language. * Added --help flag and help messages. * gitignore: ignore npm-debug.log * Fix webpack error if NODE_ENV is not defined Default to use 'development' in config/webpack/configuration.js
This commit is contained in:
		
				
					committed by
					
						 Eugen Rochko
						Eugen Rochko
					
				
			
			
				
	
			
			
			
						parent
						
							ddc34feb58
						
					
				
				
					commit
					cf0b753209
				
			
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -46,5 +46,10 @@ redis | ||||
| /public/packs | ||||
| /node_modules | ||||
|  | ||||
|  | ||||
| # Ignore npm debug log | ||||
| npm-debug.log | ||||
|  | ||||
| # Ignore Docker option files | ||||
| docker-compose.override.yml | ||||
|  | ||||
|   | ||||
| @@ -4,8 +4,8 @@ | ||||
|   "account.edit_profile": "עריכת פרופיל", | ||||
|   "account.follow": "מעקב", | ||||
|   "account.followers": "עוקבים", | ||||
|   "account.follows_you": "במעקב אחריך", | ||||
|   "account.follows": "נעקבים", | ||||
|   "account.follows_you": "במעקב אחריך", | ||||
|   "account.mention": "אזכור של @{name}", | ||||
|   "account.mute": "להשתיק את @{name}", | ||||
|   "account.posts": "הודעות", | ||||
| @@ -53,8 +53,9 @@ | ||||
|   "emoji_button.travel": "טיולים ואתרים", | ||||
|   "empty_column.community": "טור הסביבה ריק. יש לפרסם משהו כדי שדברים יתרחילו להתגלגל!", | ||||
|   "empty_column.hashtag": "אין כלום בהאשתג הזה עדיין.", | ||||
|   "empty_column.home.public_timeline": "בפרהסיה", | ||||
|   "empty_column.home": "אף אחד לא במעקב עדיין. אפשר לבקר ב{public} או להשתמש בחיפוש כדי להתחיל ולהכיר חצוצרנים אחרים.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "בפרהסיה", | ||||
|   "empty_column.notifications": "אין התראות עדיין. יאללה, הגיע הזמן להתחיל להתערבב!", | ||||
|   "empty_column.public": "אין פה כלום! כדי למלא את הטור הזה אפשר לכתוב משהו, או להתחיל לעקוב אחרי אנשים מקהילות אחרות.", | ||||
|   "follow_request.authorize": "קבלה", | ||||
| @@ -84,7 +85,6 @@ | ||||
|   "navigation_bar.public_timeline": "בפרהסיה", | ||||
|   "notification.favourite": "חצרוצך חובב על ידי {name}", | ||||
|   "notification.follow": "{name} במעקב אחרייך", | ||||
|   "notification.mention": "אוזכרת ע\"י {name}", | ||||
|   "notification.reblog": "חצרוצך הודהד על ידי {name}", | ||||
|   "notifications.clear": "הסרת התראות", | ||||
|   "notifications.clear_confirmation": "להסיר את כל ההתראות? בטוח?", | ||||
| @@ -131,7 +131,6 @@ | ||||
|   "report.submit": "שליחה", | ||||
|   "report.target": "דיווח", | ||||
|   "search.placeholder": "חיפוש", | ||||
|   "search.status_by": "הודעה מאת {name}", | ||||
|   "search_results.total": "{count, number} {count, plural, one {תוצאה} other {תוצאות}}", | ||||
|   "status.cannot_reblog": "לא ניתן להדהד הודעה זו", | ||||
|   "status.delete": "מחיקה", | ||||
| @@ -145,8 +144,8 @@ | ||||
|   "status.reply": "תגובה", | ||||
|   "status.replyAll": "תגובה לכולם", | ||||
|   "status.report": "דיווח על @{name}", | ||||
|   "status.sensitive_warning": "תוכן רגיש", | ||||
|   "status.sensitive_toggle": "לחצו כדי לראות", | ||||
|   "status.sensitive_warning": "תוכן רגיש", | ||||
|   "status.show_less": "הראה פחות", | ||||
|   "status.show_more": "הראה יותר", | ||||
|   "tabs_bar.compose": "חיבור", | ||||
| @@ -162,4 +161,4 @@ | ||||
|   "video_player.toggle_sound": "הפעלת\\ביטול שמע", | ||||
|   "video_player.toggle_visible": "הפעלת\\ביטול תצוגה", | ||||
|   "video_player.video_error": "לא ניתן לנגן וידאו" | ||||
| } | ||||
| } | ||||
| @@ -53,8 +53,9 @@ | ||||
|   "emoji_button.travel": "Podróże i miejsca", | ||||
|   "empty_column.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby odbić piłeczkę!", | ||||
|   "empty_column.hashtag": "Nie ma postów oznaczonych tym hashtagiem. Możesz napisać pierwszy!", | ||||
|   "empty_column.home.public_timeline": "publiczna oś czasu", | ||||
|   "empty_column.home": "Nie obserwujesz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć ciekawych ludzi.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "publiczna oś czasu", | ||||
|   "empty_column.notifications": "Nie masz żadnych powiadomień. Rozpocznij interakcje z innymi użytkownikami.", | ||||
|   "empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić.", | ||||
|   "follow_request.authorize": "Autoryzuj", | ||||
| @@ -84,7 +85,6 @@ | ||||
|   "navigation_bar.public_timeline": "Oś czasu federacji", | ||||
|   "notification.favourite": "{name} dodał twój status do ulubionych", | ||||
|   "notification.follow": "{name} zaczął cię obserwować", | ||||
|   "notification.mention": "{name} wspomniał o tobie", | ||||
|   "notification.reblog": "{name} podbił twój status", | ||||
|   "notifications.clear": "Wyczyść powiadomienia", | ||||
|   "notifications.clear_confirmation": "Czy na pewno chcesz bezpowrotnie usunąć wszystkie powiadomienia?", | ||||
| @@ -131,7 +131,6 @@ | ||||
|   "report.submit": "Wyślij", | ||||
|   "report.target": "Zgłaszanie", | ||||
|   "search.placeholder": "Szukaj", | ||||
|   "search.status_by": "Status od {name}", | ||||
|   "search_results.total": "{count, number} {count, plural, one {wynik} more {wyniki}}", | ||||
|   "status.cannot_reblog": "Ten post nie może zostać podbity", | ||||
|   "status.delete": "Usuń", | ||||
| @@ -161,5 +160,5 @@ | ||||
|   "video_player.expand": "Przełącz wideo", | ||||
|   "video_player.toggle_sound": "Przełącz dźwięk", | ||||
|   "video_player.toggle_visible": "Przełącz widoczność", | ||||
|   "video_player.video_error": "Nie można odtworzyć pliku wideo", | ||||
| }; | ||||
|   "video_player.video_error": "Nie można odtworzyć pliku wideo" | ||||
| } | ||||
							
								
								
									
										2
									
								
								app/javascript/mastodon/locales/whitelist_he.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/javascript/mastodon/locales/whitelist_he.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| [ | ||||
| ] | ||||
							
								
								
									
										2
									
								
								app/javascript/mastodon/locales/whitelist_pl.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/javascript/mastodon/locales/whitelist_pl.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| [ | ||||
| ] | ||||
| @@ -7,8 +7,8 @@ const { readFileSync } = require('fs') | ||||
|  | ||||
| const configPath = resolve('config', 'webpack') | ||||
| const loadersDir = join(__dirname, 'loaders') | ||||
| const paths = safeLoad(readFileSync(join(configPath, 'paths.yml'), 'utf8'))[env.NODE_ENV] | ||||
| const devServer = safeLoad(readFileSync(join(configPath, 'development.server.yml'), 'utf8'))[env.NODE_ENV] | ||||
| const paths = safeLoad(readFileSync(join(configPath, 'paths.yml'), 'utf8'))[env.NODE_ENV || 'development'] | ||||
| const devServer = safeLoad(readFileSync(join(configPath, 'development.server.yml'), 'utf8'))[env.NODE_ENV || 'development'] | ||||
|  | ||||
| // Compute public path based on environment and CDN_HOST in production | ||||
| const ifHasCDN = env.CDN_HOST !== undefined && env.NODE_ENV === 'production' | ||||
|   | ||||
| @@ -1,34 +1,84 @@ | ||||
| /*eslint no-console: "off"*/ | ||||
| const manageTranslations = require('react-intl-translations-manager').default; | ||||
| const argv = require('minimist')(process.argv.slice(2)); | ||||
| const fs = require('fs'); | ||||
|  | ||||
| const translationsDirectory = 'app/javascript/mastodon/locales'; | ||||
| const localeFn = /^([a-z]{2,3}(|\-[A-Z]+))\.json$/; | ||||
| const reRFC5646 = /^[a-z]{2,3}(|\-[A-Z]+)$/; | ||||
| const availableLanguages = fs.readdirSync(`${process.cwd()}/${translationsDirectory}`).reduce((acc, fn) => { | ||||
|   if (fn.match(localeFn)) { | ||||
|     acc.push(fn.replace(localeFn, '$1')); | ||||
|   } | ||||
|   return acc; | ||||
| }, []); | ||||
|  | ||||
| // print help message | ||||
| if (argv.help !== undefined) { | ||||
|   console.log( | ||||
| `Usage: yarn manage:translations -- [OPTIONS] [LANGUAGES] | ||||
|  | ||||
| Manage javascript translation files in mastodon. Generates and update | ||||
| translations in translationsDirectory: ${translationsDirectory} | ||||
|  | ||||
| OPTIONS | ||||
|   --help    show this message | ||||
|   --force   force using the provided languages. create files if not exists. | ||||
|             default: false | ||||
|  | ||||
| LANGUAGES | ||||
| The RFC5646 language tag for the language you want to test or fix. If you want | ||||
| to input multiple languages, separate them with space. | ||||
|  | ||||
| Available languages: | ||||
| ${availableLanguages} | ||||
| `); | ||||
|   process.exit(0); | ||||
| } | ||||
|  | ||||
| // determine the languages list | ||||
| const languages = (argv._.length === 0) ? availableLanguages : argv._; | ||||
|  | ||||
| // check if the languages provided are RFC5626 compliant | ||||
| (function() { | ||||
|   let invalidLanguages = languages.reduce((acc, language) => { | ||||
|     if (!language.match(reRFC5646)) { | ||||
|       acc.push(language); | ||||
|     } | ||||
|     return acc; | ||||
|   }, []); | ||||
|   if (invalidLanguages.length > 0) { | ||||
|     console.log(`Error:`); | ||||
|     for (let language of invalidLanguages) { | ||||
|       console.error(`* Not RFC5626 name: ${language}`); | ||||
|     } | ||||
|     console.log(`\nUse yarn "manage:translations -- --help" for usage information\n`); | ||||
|     process.exit(1); | ||||
|   } | ||||
| })(); | ||||
|  | ||||
| // make sure the language exists. Unless force to create locale file. | ||||
| if (argv.force !== true) { | ||||
|   let invalidLanguages = languages.reduce((acc, language) => { | ||||
|     if (availableLanguages.indexOf(language) < 0) { | ||||
|       acc.push(language); | ||||
|     } | ||||
|     return acc; | ||||
|   }, []); | ||||
|   if (invalidLanguages.length > 0) { | ||||
|     console.log(`Error:`); | ||||
|     for (let language of invalidLanguages) { | ||||
|       console.error(`* Language not available: ${language}`); | ||||
|     } | ||||
|     console.log(`\nIf you want to force creating the language(s) above, please add the --force option.\n`); | ||||
|     process.exit(1); | ||||
|   } | ||||
| } | ||||
|  | ||||
| manageTranslations({ | ||||
|   messagesDirectory: 'build/messages', | ||||
|   translationsDirectory: 'app/javascript/mastodon/locales/', | ||||
|   translationsDirectory, | ||||
|   detectDuplicateIds: false, | ||||
|   singleMessagesFile: true, | ||||
|   languages: [ | ||||
|     'ar', | ||||
|     'en', | ||||
|     'de', | ||||
|     'es', | ||||
|     'fa', | ||||
|     'hr', | ||||
|     'hu', | ||||
|     'io', | ||||
|     'it', | ||||
|     'fr', | ||||
|     'nl', | ||||
|     'no', | ||||
|     'oc', | ||||
|     'pt', | ||||
|     'pt-BR', | ||||
|     'uk', | ||||
|     'fi', | ||||
|     'eo', | ||||
|     'ru', | ||||
|     'ja', | ||||
|     'zh-HK', | ||||
|     'zh-CN', | ||||
|     'bg', | ||||
|     'id', | ||||
|   ], | ||||
| }) | ||||
|   languages, | ||||
| }); | ||||
|   | ||||
| @@ -3,6 +3,8 @@ | ||||
|   "license": "AGPL-3.0", | ||||
|   "scripts": { | ||||
|     "postversion": "git push --tags", | ||||
|     "build:development": "NODE_ENV=development yarn webpack -- --config config/webpack/development.js", | ||||
|     "build:production": "NODE_ENV=production yarn webpack -- --config config/webpack/production.js", | ||||
|     "manage:translations": "node ./config/webpack/translationRunner.js", | ||||
|     "start": "babel-node ./streaming/index.js --presets es2015,stage-2", | ||||
|     "storybook": "start-storybook -p 9001 -c storybook", | ||||
| @@ -113,6 +115,7 @@ | ||||
|     "eslint-plugin-jsx-a11y": "^4.0.0", | ||||
|     "eslint-plugin-react": "^6.10.3", | ||||
|     "jsdom": "^9.11.0", | ||||
|     "minimist": "^1.2.0", | ||||
|     "mocha": "^3.2.0", | ||||
|     "react-intl-translations-manager": "^5.0.0", | ||||
|     "webpack-dev-server": "^2.4.5" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user