-
Notifications
You must be signed in to change notification settings - Fork 1
/
makeTranslationFileContentsForRepo.js
202 lines (168 loc) · 7.67 KB
/
makeTranslationFileContentsForRepo.js
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Copyright 2022, University of Colorado Boulder
/**
* Export a function that returns the translation file contents for a given repo and translation.
*
* @author Liam Mulhall <liammulh@gmail.com>
*/
import axios from 'axios';
import getSimMetadata from './getSimMetadata.js';
import getSimNamesAndTitles from './getSimNamesAndTitles.js';
import getTranslatedStringFileUrl from './getTranslatedStringFileUrl.js';
import logger from './logger.js';
/**
* For a given repo, return an object that looks like:
*
* {
* "stringKeyA": {
* "value": "string key A value",
* "history": [
* {
* "userId": 123456,
* "timestamp": 1653082097154,
* "oldValue": "",
* "newValue": "string key A value",
* "explanation": null
* }
* ]
* },
* "stringKeyB": { ... },
* ...
* }
*
* Essentially, it's the translation file contents for the given repo based on the translation provided.
*
* @param repo - the repo name in lowercase-kebab (repo-style)
* @param translation - the translation object obtained from the client side
* @returns {Promise<Object>}
*/
const makeTranslationFileContentsForRepo = async ( repo, translation ) => {
logger.info( `making translation file contents for ${repo}` );
const translationFileContentsForRepo = {};
// Get old translation file if one exists.
const oldTranslationFileUrl = getTranslatedStringFileUrl( repo, translation.locale );
let oldTranslationFile = null;
try {
const oldTranslationFileRes = await axios.get( oldTranslationFileUrl );
if ( oldTranslationFileRes.status !== 404 ) {
oldTranslationFile = oldTranslationFileRes.data;
}
}
catch( e ) {
if ( e.response.status === 404 ) {
logger.warn( `no translation file for ${translation.locale}/${repo}` );
}
else {
logger.error( e );
}
}
// Get list of sim names for checking if we're dealing with shared strings. Note how
// we're passing true for the second argument. This is to say we're a team member, so
// please get all the sims, even the ones that wouldn't normally be visible, e.g. Bumper.
// For context on this, see https://github.com/phetsims/rosetta/issues/360 and
// https://github.com/phetsims/rosetta/issues/361.
const simMetadata = await getSimMetadata();
const simNames = Object.keys( getSimNamesAndTitles( simMetadata, 'true' ) );
// Set translation form data variable.
let translationFormData = {};
if ( repo === translation.simName ) {
// We're dealing with sim-specific strings.
translationFormData = translation.translationFormData.simSpecific;
}
else if ( simNames.includes( repo ) ) {
// We're dealing with shared strings.
translationFormData = translation.translationFormData.shared;
}
else {
// We're dealing with common strings.
translationFormData = translation.translationFormData.common;
}
for ( const stringKey of Object.keys( translationFormData ) ) {
if ( repo === translation.simName || repo === translationFormData[ stringKey ].repo ) {
// Trim leading and trailing whitespace.
// NOTE: If a user deliberately wants a space, this will change
// the string from ' ' to '', which makes the string fall back
// to English.
translationFormData[ stringKey ].translated = translationFormData[ stringKey ].translated.trim();
const stringNotYetTranslated = !oldTranslationFile ||
Object.keys( oldTranslationFile ).length === 0 ||
oldTranslationFile[ stringKey ] === '' ||
!oldTranslationFile[ stringKey ];
const translationLeftBlank = stringNotYetTranslated &&
translationFormData[ stringKey ].translated === '';
const userProvidedInitialTranslation = stringNotYetTranslated &&
translationFormData[ stringKey ].translated !== '';
const stringHasNonBlankTranslation = oldTranslationFile &&
oldTranslationFile[ stringKey ] &&
oldTranslationFile[ stringKey ].value !== '';
const translationErased = stringHasNonBlankTranslation &&
translationFormData[ stringKey ].translated === '';
const translationUntouched = stringHasNonBlankTranslation &&
translationFormData[ stringKey ].translated === oldTranslationFile[ stringKey ].value;
const translationModified = stringHasNonBlankTranslation &&
translationFormData[ stringKey ].translated !== oldTranslationFile[ stringKey ].value;
if ( translationLeftBlank ) {
logger.verbose( `string for ${stringKey} not translated; not adding it to the translation file for ${repo}` );
}
else if ( userProvidedInitialTranslation ) {
logger.verbose( `user provided translation for previously untranslated string; adding ${stringKey}'s info to the translation file for ${repo}` );
// Populate history object.
const newHistoryEntry = {
userId: translation.userId,
timestamp: translation.timestamp,
oldValue: '',
newValue: translationFormData[ stringKey ].translated
};
// Add translated value and history to translation file.
translationFileContentsForRepo[ stringKey ] = {
value: translationFormData[ stringKey ].translated,
history: [ newHistoryEntry ]
};
}
else if ( translationErased ) {
logger.verbose( `blank value submitted for previously translated string ${stringKey}; submitting blank value` );
const newHistoryEntry = {
userId: translation.userId,
timestamp: translation.timestamp,
oldValue: '',
newValue: translationFormData[ stringKey ].translated
};
translationFileContentsForRepo[ stringKey ] = {
value: translationFormData[ stringKey ].translated,
history: [ newHistoryEntry ]
};
}
else if ( translationUntouched ) {
logger.verbose( `the translation for ${stringKey}'s string was untouched; using old entry` );
translationFileContentsForRepo[ stringKey ] = oldTranslationFile[ stringKey ];
}
else if ( translationModified ) {
logger.verbose( `the translation for ${stringKey}'s string was modified; adding it to the translation file for ${repo}` );
// Populate history object.
const newHistoryEntry = {
userId: translation.userId,
timestamp: translation.timestamp,
oldValue: oldTranslationFile[ stringKey ].value,
newValue: translationFormData[ stringKey ].translated,
explanation: null // this is no longer used, but for some reason we keep it around
};
// Add the history entry to the history array.
const newHistory = oldTranslationFile[ stringKey ].history.concat( [ newHistoryEntry ] );
// Add translated value and history to translation file.
translationFileContentsForRepo[ stringKey ] = {
value: translationFormData[ stringKey ].translated,
history: newHistory
};
}
else {
// We've hit some sort of weird corner case.
logger.error( `none of the scenarios for ${stringKey} were true; something has gone horribly wrong` );
}
}
}
logger.info( `made translation file contents for ${repo}; returning them` );
if ( JSON.stringify( translationFileContentsForRepo ) === JSON.stringify( oldTranslationFile ) ) {
return {};
}
return translationFileContentsForRepo;
};
export default makeTranslationFileContentsForRepo;