|
1 | 1 | // Library requires
|
2 | 2 | const i18Config = require("../../i18n/config.json")
|
3 |
| -const { copyFileSync, existsSync, mkdirSync, readdirSync } = require("fs") |
| 3 | +const { |
| 4 | + copyFileSync, |
| 5 | + existsSync, |
| 6 | + mkdirSync, |
| 7 | + readdirSync, |
| 8 | + readFileSync, |
| 9 | +} = require("fs") |
4 | 10 | const { resolve, join } = require("path")
|
5 | 11 | const argv = require("minimist")(process.argv.slice(2))
|
| 12 | + |
| 13 | +/****************************** |
| 14 | + * Console flags * |
| 15 | + ******************************/ |
| 16 | + |
6 | 17 | /**
|
7 |
| - * Console flags |
8 | 18 | * -v,--verbose Prints verbose console logs
|
9 | 19 | * -f,--full Prints full name of buckets in summary
|
| 20 | + */ |
| 21 | + |
| 22 | +/****************************** |
| 23 | + * Instructions for use * |
| 24 | + ******************************/ |
| 25 | + |
| 26 | +/** |
| 27 | + * 1. Run `yarn crowdin-clean` to initialize fresh ./.crowdin folder. This can also be used to erase contents when finished. |
10 | 28 | *
|
| 29 | + * 2a. Export/import CSV of languages ready for review: |
| 30 | + * 1. Open "Website translation board" document in ethereum.org Notion (internal only) |
| 31 | + * 2. Switch view of "Translation status by language" table to "All reviewed" |
| 32 | + * 3. Click triple-dot (...) menu in TOP right corner of the entire app |
| 33 | + * 4. Select "Export" > "Export as CSV" |
| 34 | + * Export format: Markdown & CSV |
| 35 | + * Include databases: Current view |
| 36 | + * Include content: No files or images |
| 37 | + * Include subpages: Off |
| 38 | + * Click "Export" > Save zip file |
| 39 | + * 5. Unzip contents into (or copy into) ./.crowdin folder in the root of this repo |
11 | 40 | *
|
12 |
| - * Follow these steps to import translations from Crowdin export: |
13 |
| - * |
14 |
| - * 1. Copy languages folder from Crowdin export to ./.crowdin |
15 |
| - * ie. ./.crowdin/{lang-codes} |
16 |
| - * Tip: Run `yarn crowdin-clean` to initialize the `.crowdin` folder. Can |
17 |
| - * also be used to erase contents when finished. |
18 |
| - * |
19 |
| - * 2. Select buckets to import by adding the number of the corresponding |
20 |
| - * content bucket to the chosen language array below |
21 |
| - * ie. `es: [1, 10],` would import the "Homepage" and "Learn" buckets for Spanish |
22 |
| - * |
23 |
| - * 3. Save file without committing |
| 41 | + * 2b. Alternatively, you can manually add buckets to import to the USER_OVERRIDE object below. |
| 42 | + * 1. Add the number of the corresponding content bucket to the chosen language array below |
| 43 | + * ie. `es: [1, 10],` would import the "Homepage" and "Learn" buckets for Spanish |
| 44 | + * 2. Save file without committing* |
24 | 45 | *
|
25 |
| - * 4. Execute script by running `yarn crowdin-import` |
| 46 | + * Export/import translated content from Crowdin: |
| 47 | + * 1. Export latest translated content from Crowdin and unzip |
| 48 | + * 2. Copy languages folder from Crowdin export to ./.crowdin |
| 49 | + * ie. ./.crowdin/{lang-codes} |
26 | 50 | *
|
27 |
| - * 5. If successful, copy `GATSBY_BUILD_LANGS={langs}` output and paste in |
28 |
| - * your `.env`, then build site to test results. |
| 51 | + * Execute script: |
| 52 | + * 1. Execute script by running `yarn crowdin-import` |
| 53 | + * 2. If successful, copy `GATSBY_BUILD_LANGS={langs}` output and paste in |
| 54 | + * your `.env`, then build site to test results. |
29 | 55 | *
|
30 |
| - * Remember: Revert working changes to this file before committing Crowdin import |
| 56 | + * *Remember: Revert any working changes to this file before committing Crowdin import |
31 | 57 | */
|
32 | 58 |
|
33 |
| -type UserSelectionObject = { [key: string]: Array<number> } |
34 |
| -const USER_SELECTION: UserSelectionObject = { |
| 59 | +type BucketsList = { [key: string]: Array<number> } |
| 60 | +const USER_OVERRIDE: BucketsList = { |
35 | 61 | ar: [],
|
36 | 62 | az: [],
|
37 | 63 | bg: [],
|
@@ -96,10 +122,15 @@ const USER_SELECTION: UserSelectionObject = {
|
96 | 122 | * slight from those used in the repo). These folders must be copied into the
|
97 | 123 | * root `.crowdin` folder of this repo.
|
98 | 124 | *
|
99 |
| - * Using the USER_SELECTION object above, the script iterates through each |
100 |
| - * language chosen, using the dictionary object below to convert the repo lang |
101 |
| - * code to the code used by Crowdin (only if needed, defaults to same). `fs` |
102 |
| - * is used to find matching language folder. |
| 125 | + * A CSV containing the language buckets that have been "Reviewed" can be exported |
| 126 | + * from Crowdin to automate the process of importing the needed buckets. See |
| 127 | + * "Instructions for use" above. |
| 128 | + * |
| 129 | + * You can alternative use the USER_OVERRIDE object above to manually select buckets. |
| 130 | + * |
| 131 | + * The script iterates through each language chosen, using the dictionary object |
| 132 | + * below to convert the repo lang code to the code used by Crowdin (only if needed, |
| 133 | + * defaults to same). `fs` is used to find matching language folder. |
103 | 134 | *
|
104 | 135 | * The "buckets" chosen (type number[]) are then iterated over, opening the
|
105 | 136 | * corresponding folder that begins with the same number string (formatted 00).
|
@@ -131,7 +162,7 @@ if (!existsSync(crowdinRoot)) mkdirSync(crowdinRoot)
|
131 | 162 | * This is used to convert any codes that may differ when performing folder lookup.
|
132 | 163 | */
|
133 | 164 | const getCrowdinCode = (code: string): string =>
|
134 |
| - i18Config.filter((lang) => lang.code === code)?.[0].crowdinCode || code |
| 165 | + i18Config.filter((lang) => lang.code === code)?.[0]?.crowdinCode || code |
135 | 166 |
|
136 | 167 | /**
|
137 | 168 | * Names for each bucket in order, zero indexed.
|
@@ -183,6 +214,67 @@ const trackers: TrackerObject = {
|
183 | 214 | const log = (message: any, ...optionalParams: any): void => {
|
184 | 215 | VERBOSE && console.log(message, ...optionalParams)
|
185 | 216 | }
|
| 217 | + |
| 218 | +/** |
| 219 | + * Fetches CSV exported from Notion "Website translation board" table |
| 220 | + * See above for details on how to export CSV and import into repo |
| 221 | + * @returns Object containing language codes as keys, and an array of bucket numbers to be imported |
| 222 | + */ |
| 223 | +const fetchReviewedCsv = (): BucketsList => { |
| 224 | + const csvDir: string = readdirSync(crowdinRoot).filter((dir: string) => |
| 225 | + dir.startsWith("Website translation board") |
| 226 | + )[0] |
| 227 | + if (!csvDir) return {} |
| 228 | + const path = join(crowdinRoot, csvDir) |
| 229 | + const reviewedCsvPath: Array<string> = readdirSync(path).filter( |
| 230 | + (file: string) => { |
| 231 | + const fileParts: Array<string> = file.split(".") |
| 232 | + return ( |
| 233 | + fileParts[0].startsWith("https") && |
| 234 | + !fileParts[0].endsWith("all") && |
| 235 | + fileParts[1] === "csv" |
| 236 | + ) |
| 237 | + } |
| 238 | + )[0] |
| 239 | + const bucketsList: BucketsList = {} |
| 240 | + const csvFile = readFileSync(join(path, reviewedCsvPath), "utf8") |
| 241 | + if (!csvFile) return {} |
| 242 | + const data = csvFile.split("\n").map((row: string) => { |
| 243 | + const quotePair = /"([^"]+)"/g |
| 244 | + const sanitized = row.replaceAll(quotePair, (match) => |
| 245 | + match.replace(",", " ").replace(/"/g, "") |
| 246 | + ) |
| 247 | + return sanitized.split(",") |
| 248 | + }) |
| 249 | + const headings = data.shift() |
| 250 | + const langCodeIndex = headings.indexOf("code") |
| 251 | + const firstBucketIndex = headings.findIndex((item: string) => |
| 252 | + item.startsWith("1)") |
| 253 | + ) |
| 254 | + data.forEach((rowItems: Array<string>) => { |
| 255 | + const langCode = rowItems[langCodeIndex].split(" ").at(-1) // "es-EM → es" parses to "es" |
| 256 | + if (!langCode) return |
| 257 | + const bucketsForLang: Array<number> = [] |
| 258 | + rowItems.forEach((item: string, idx: number) => { |
| 259 | + if (item.includes("Reviewed")) |
| 260 | + bucketsForLang.push(idx - firstBucketIndex + 1) |
| 261 | + }) |
| 262 | + bucketsList[langCode] = bucketsForLang |
| 263 | + }) |
| 264 | + return bucketsList |
| 265 | +} |
| 266 | + |
| 267 | +/** |
| 268 | + * If any buckets are selected in USER_OVERRIDE, use those instead of importing from CSV. |
| 269 | + */ |
| 270 | +const useUserOverRide = |
| 271 | + Object.values(USER_OVERRIDE).filter((buckets) => buckets.length > 0).length > |
| 272 | + 0 |
| 273 | + |
| 274 | +const bucketsToImport: BucketsList = useUserOverRide |
| 275 | + ? USER_OVERRIDE |
| 276 | + : fetchReviewedCsv() |
| 277 | + |
186 | 278 | /**
|
187 | 279 | * Reads `ls` file contents of `_path`, moving .md and .json files
|
188 | 280 | * to their corresponding destinations in the repo. Function is called
|
@@ -253,16 +345,16 @@ type SelectionItem = {
|
253 | 345 | crowdinLangCode: string
|
254 | 346 | buckets: Array<number>
|
255 | 347 | }
|
256 |
| -const importSelection: Array<SelectionItem> = Object.keys(USER_SELECTION) |
| 348 | +const importSelection: Array<SelectionItem> = Object.keys(bucketsToImport) |
257 | 349 | .filter((repoLangCode: string): boolean => {
|
258 |
| - if (!USER_SELECTION[repoLangCode].length) trackers.emptyBuckets++ |
259 |
| - return !!USER_SELECTION[repoLangCode].length |
| 350 | + if (!bucketsToImport[repoLangCode].length) trackers.emptyBuckets++ |
| 351 | + return !!bucketsToImport[repoLangCode].length |
260 | 352 | })
|
261 | 353 | .map(
|
262 | 354 | (repoLangCode: string): SelectionItem => ({
|
263 | 355 | repoLangCode,
|
264 | 356 | crowdinLangCode: getCrowdinCode(repoLangCode),
|
265 |
| - buckets: USER_SELECTION[repoLangCode], |
| 357 | + buckets: bucketsToImport[repoLangCode], |
266 | 358 | })
|
267 | 359 | )
|
268 | 360 |
|
|
0 commit comments