@terraware/web-components 5.0.27-rc.0 → 5.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/AntSwitch/index.js +22 -19
- package/components/Autocomplete/Autocomplete.js +130 -106
- package/components/Badge/index.js +29 -28
- package/components/BusySpinner/index.js +45 -40
- package/components/Button/Button.js +47 -41
- package/components/Checkbox.js +63 -64
- package/components/Confirm/index.js +83 -74
- package/components/DatePicker/DatePicker.js +141 -138
- package/components/DialogBox/DialogBox.js +118 -111
- package/components/Divisor.js +18 -18
- package/components/Dropdown.js +94 -66
- package/components/EditableTable/index.js +271 -211
- package/components/ErrorBox/ErrorBox.js +49 -47
- package/components/FileChooser/index.js +135 -117
- package/components/FormBottomBar/index.js +84 -75
- package/components/Icon/Icon.js +20 -12
- package/components/Icon/icons/index.js +256 -256
- package/components/IconTooltip/index.js +60 -49
- package/components/Map/MapBox.js +452 -545
- package/components/Map/MapContainer.js +29 -25
- package/components/Map/MapDateSliderControl.js +64 -64
- package/components/Map/MapDrawer.js +47 -41
- package/components/Map/MapLegend.js +172 -176
- package/components/Map/MapViewStyleControl.js +63 -59
- package/components/Map/ReactMapboxWrapper.js +2 -3
- package/components/Map/index.js +180 -161
- package/components/Map/types.js +17 -12
- package/components/Map/useMaintainLayerOrder.js +26 -16
- package/components/Markdown/index.js +26 -25
- package/components/Message/Message.js +76 -73
- package/components/MultiSelect/index.js +124 -112
- package/components/Navbar/NavFooter.js +13 -9
- package/components/Navbar/NavItem.js +76 -70
- package/components/Navbar/NavSection.js +19 -17
- package/components/Navbar/Navbar.js +46 -39
- package/components/Navbar/SubNavbar.js +13 -9
- package/components/Note.js +22 -18
- package/components/OverlayModal/OverlayModal.js +63 -56
- package/components/PageForm/index.js +41 -33
- package/components/PhotoChooser/index.js +173 -165
- package/components/PhotosCarousel/PhotoSlide.js +35 -32
- package/components/PhotosCarousel/index.js +85 -80
- package/components/PhotosCarousel/usePhotosCarousel.js +76 -69
- package/components/Pill/index.js +45 -41
- package/components/PillList/index.js +24 -17
- package/components/PlacementWrapper/index.js +59 -38
- package/components/PopoverMenu/Popover.js +85 -68
- package/components/PopoverMenu/index.js +56 -51
- package/components/PopoverMultiSelect/index.js +59 -52
- package/components/ProgressCircle/ProgressCircle.js +32 -28
- package/components/RadioButton.js +19 -17
- package/components/Select/Select.js +15 -13
- package/components/Select/SelectT.js +250 -235
- package/components/Separator/index.js +17 -14
- package/components/Size.js +1 -0
- package/components/Slider/index.js +56 -43
- package/components/SummaryBox.js +72 -70
- package/components/Tabs/index.js +135 -133
- package/components/TextTruncated/index.js +130 -117
- package/components/Textfield/Textfield.js +190 -158
- package/components/Textfield/TruncatedTextArea.js +84 -70
- package/components/TimelineSlider/TimelineMarker.js +30 -25
- package/components/TimelineSlider/TimelineRail.js +11 -8
- package/components/TimelineSlider/index.js +66 -64
- package/components/TimelineSliderV2/ClusterBadge.js +29 -25
- package/components/TimelineSliderV2/TimelineDot.js +35 -31
- package/components/TimelineSliderV2/TimelineRail.js +11 -8
- package/components/TimelineSliderV2/index.js +103 -121
- package/components/TimelineSliderV2/layout.js +147 -124
- package/components/TimelineSliderV2/types.js +1 -0
- package/components/Tooltip/Tooltip.js +56 -47
- package/components/ViewPhotosDialog/index.js +35 -28
- package/components/VirtualWalkthrough/Annotation.js +207 -163
- package/components/VirtualWalkthrough/AnnotationEditPane.js +184 -198
- package/components/VirtualWalkthrough/AnnotationPanel.js +199 -187
- package/components/VirtualWalkthrough/Application.js +34 -31
- package/components/VirtualWalkthrough/AutoRotator.js +223 -85
- package/components/VirtualWalkthrough/BlockingSpinner.js +28 -26
- package/components/VirtualWalkthrough/BoundaryRing.js +34 -30
- package/components/VirtualWalkthrough/BoundaryWall.js +36 -32
- package/components/VirtualWalkthrough/CameraInfo.js +89 -100
- package/components/VirtualWalkthrough/ControlsInfoPane.js +195 -254
- package/components/VirtualWalkthrough/GradientSky.js +73 -72
- package/components/VirtualWalkthrough/SplatControls.js +221 -209
- package/components/VirtualWalkthrough/SplatCrop.js +20 -21
- package/components/VirtualWalkthrough/SplatFadeCrop.js +20 -21
- package/components/VirtualWalkthrough/SplatModel.js +85 -65
- package/components/VirtualWalkthrough/SplatRevealRain.js +46 -41
- package/components/VirtualWalkthrough/TfAnnotationManager.js +379 -197
- package/components/VirtualWalkthrough/TfXrNavigation.js +162 -73
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +431 -438
- package/components/VirtualWalkthrough/VrAnnotationPanel.js +22 -18
- package/components/VirtualWalkthrough/XrAnnotationInteraction.js +17 -14
- package/components/VirtualWalkthrough/XrExitButton.js +17 -14
- package/components/VirtualWalkthrough/XrGazeDwell.js +17 -14
- package/components/VirtualWalkthrough/XrPointerRay.js +17 -14
- package/components/VirtualWalkthrough/XrStartPose.js +121 -67
- package/components/VirtualWalkthrough/boundary-ring.js +117 -87
- package/components/VirtualWalkthrough/boundary-wall.js +258 -175
- package/components/VirtualWalkthrough/camera-adoption.js +64 -18
- package/components/VirtualWalkthrough/groundPlane.js +47 -32
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +35 -35
- package/components/VirtualWalkthrough/splatFormat.js +47 -13
- package/components/VirtualWalkthrough/useAdoptedCamera.js +44 -23
- package/components/VirtualWalkthrough/useRestartOnVr.js +37 -26
- package/components/VirtualWalkthrough/useXrRenderTuning.js +54 -33
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +99 -67
- package/components/VirtualWalkthrough/vr-annotation-panel.js +433 -403
- package/components/VirtualWalkthrough/vr-annotation-scroll.js +28 -10
- package/components/VirtualWalkthrough/walkthrough-camera.js +329 -204
- package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.js +80 -39
- package/components/VirtualWalkthrough/xr-annotation-candidates.js +18 -6
- package/components/VirtualWalkthrough/xr-annotation-interaction.js +56 -31
- package/components/VirtualWalkthrough/xr-annotation-targeting.js +38 -22
- package/components/VirtualWalkthrough/xr-button-arming.js +17 -7
- package/components/VirtualWalkthrough/xr-button-hold.js +36 -22
- package/components/VirtualWalkthrough/xr-exit-button-mask.js +36 -22
- package/components/VirtualWalkthrough/xr-exit-button.js +207 -148
- package/components/VirtualWalkthrough/xr-face-buttons.js +29 -19
- package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +34 -23
- package/components/VirtualWalkthrough/xr-gaze-dwell.js +144 -107
- package/components/VirtualWalkthrough/xr-interactive-ui.js +24 -12
- package/components/VirtualWalkthrough/xr-pointer-ray.js +20 -13
- package/components/VirtualWalkthrough/xr-progress-pie-material.js +88 -117
- package/components/VirtualWalkthrough/xr-progress-pie.js +10 -1
- package/components/VirtualWalkthrough/xr-ray-quad.js +30 -16
- package/components/VirtualWalkthrough/xr-render-tuning.js +48 -17
- package/components/VirtualWalkthrough/xr-scene-bounds.js +37 -21
- package/components/VirtualWalkthrough/xr-start-pose.js +73 -27
- package/components/VirtualWalkthrough/xr-teleport-gesture.js +42 -16
- package/components/svg/BlobbyGrayIconImage.js +39 -41
- package/components/svg/BlobbyGrayIconImport.js +39 -41
- package/components/svg/BlobbyGrayIconUploadToTheCloud.js +39 -41
- package/components/svg/BlobbyIconCloudChecked.js +164 -202
- package/components/svg/BlobbyIconExportOrUploadPhoto.js +164 -202
- package/components/svg/BlobbyIconFolder.js +164 -202
- package/components/svg/BlobbyIconGraphReport.js +164 -202
- package/components/svg/BlobbyIconHappy.js +164 -202
- package/components/svg/BlobbyIconHeartMonitor.js +164 -202
- package/components/svg/BlobbyIconLeaf.js +164 -202
- package/components/svg/BlobbyIconLibrary.js +164 -202
- package/components/svg/BlobbyIconNursery.js +166 -204
- package/components/svg/BlobbyIconOrganization.js +164 -202
- package/components/svg/BlobbyIconParchment.js +164 -202
- package/components/svg/BlobbyIconPeople.js +164 -202
- package/components/svg/BlobbyIconSeedBank.js +164 -202
- package/components/svg/BlobbyIconSeedling.js +164 -202
- package/components/svg/BlobbyIconSeeds.js +164 -202
- package/components/svg/BlobbyIconSite.js +167 -206
- package/components/svg/BlobbyIconSparkles.js +164 -202
- package/components/svg/BlobbyIconWrench.js +164 -202
- package/components/svg/Bug.js +21 -19
- package/components/svg/Calendar.js +21 -19
- package/components/svg/CaretDown.js +21 -19
- package/components/svg/CaretLeft.js +21 -19
- package/components/svg/CaretRight.js +21 -19
- package/components/svg/CaretUp.js +21 -19
- package/components/svg/ChevronDown.js +21 -19
- package/components/svg/ChevronUp.js +21 -19
- package/components/svg/Close.js +21 -19
- package/components/svg/Critical.js +21 -19
- package/components/svg/Edit.js +164 -202
- package/components/svg/Error.js +21 -19
- package/components/svg/Filter.js +21 -19
- package/components/svg/Folder.js +21 -19
- package/components/svg/Help.js +21 -19
- package/components/svg/Home.js +21 -19
- package/components/svg/IconAdd.js +21 -19
- package/components/svg/IconArrowRight.js +23 -21
- package/components/svg/IconBusinessNetwork.js +21 -19
- package/components/svg/IconCalendar.js +21 -19
- package/components/svg/IconCancel.js +21 -19
- package/components/svg/IconChargingBattery.js +21 -19
- package/components/svg/IconChecklist.js +23 -21
- package/components/svg/IconCheckmark.js +21 -19
- package/components/svg/IconCoinInHand.js +21 -19
- package/components/svg/IconColumns.js +21 -19
- package/components/svg/IconComment.js +22 -20
- package/components/svg/IconDashboard.js +21 -19
- package/components/svg/IconDataMigration.js +21 -19
- package/components/svg/IconDocument.js +23 -21
- package/components/svg/IconDownloadFromTheCloud.js +23 -21
- package/components/svg/IconEdit.js +23 -21
- package/components/svg/IconExpand.js +23 -21
- package/components/svg/IconExport.js +21 -19
- package/components/svg/IconExternalLink.js +21 -19
- package/components/svg/IconEye.js +21 -19
- package/components/svg/IconEyeOff.js +21 -19
- package/components/svg/IconFile.js +21 -19
- package/components/svg/IconFolder.js +21 -19
- package/components/svg/IconFullScreen.js +22 -20
- package/components/svg/IconFutures.js +21 -19
- package/components/svg/IconGraphReport.js +21 -19
- package/components/svg/IconGrid.js +21 -19
- package/components/svg/IconHeartMonitor.js +21 -19
- package/components/svg/IconHelp.js +23 -21
- package/components/svg/IconHistory.js +21 -19
- package/components/svg/IconImport.js +21 -19
- package/components/svg/IconIndex.js +21 -19
- package/components/svg/IconLayers.js +21 -19
- package/components/svg/IconLibrary.js +22 -20
- package/components/svg/IconList.js +21 -19
- package/components/svg/IconLivePlant.js +21 -19
- package/components/svg/IconLocations.js +21 -19
- package/components/svg/IconMail.js +21 -19
- package/components/svg/IconManager.js +21 -19
- package/components/svg/IconMap.js +21 -19
- package/components/svg/IconMarker.js +21 -19
- package/components/svg/IconMenu.js +21 -19
- package/components/svg/IconMenuHorizontal.js +21 -19
- package/components/svg/IconModule.js +21 -19
- package/components/svg/IconMyLocation.js +21 -19
- package/components/svg/IconNote.js +23 -21
- package/components/svg/IconNursery.js +23 -21
- package/components/svg/IconOrg.js +21 -19
- package/components/svg/IconParchment.js +21 -19
- package/components/svg/IconPhoto.js +21 -19
- package/components/svg/IconPlantsFilled.js +21 -19
- package/components/svg/IconRedo.js +21 -19
- package/components/svg/IconRestorationSite.js +21 -19
- package/components/svg/IconSeedBank.js +21 -19
- package/components/svg/IconSeedling.js +21 -19
- package/components/svg/IconSettings.js +21 -19
- package/components/svg/IconSlice.js +21 -19
- package/components/svg/IconSubmit.js +21 -19
- package/components/svg/IconSubtract.js +23 -21
- package/components/svg/IconSynced.js +21 -19
- package/components/svg/IconTrashCan.js +21 -19
- package/components/svg/IconTreasureMap.js +21 -19
- package/components/svg/IconUnavailable.js +21 -19
- package/components/svg/IconUndo.js +21 -19
- package/components/svg/IconVariable.js +21 -19
- package/components/svg/IconVideo.js +31 -30
- package/components/svg/IconWifi.js +21 -19
- package/components/svg/Info.js +21 -19
- package/components/svg/Key.js +21 -19
- package/components/svg/Leaf.js +21 -19
- package/components/svg/Lock.js +21 -19
- package/components/svg/Logo.js +43 -48
- package/components/svg/MenuVertical.js +21 -19
- package/components/svg/Notification.js +21 -19
- package/components/svg/Person.js +21 -19
- package/components/svg/Plus.js +21 -19
- package/components/svg/RestorationSite.js +21 -19
- package/components/svg/Search.js +21 -19
- package/components/svg/Seeds.js +21 -19
- package/components/svg/Site.js +21 -19
- package/components/svg/Sparkles.js +21 -19
- package/components/svg/Species.js +21 -19
- package/components/svg/Species2.js +164 -202
- package/components/svg/Spinner.js +48 -54
- package/components/svg/Star.js +21 -19
- package/components/svg/Success.js +21 -19
- package/components/svg/SuccessFilled.js +21 -19
- package/components/svg/TerrawareLogoDesktop.js +49 -55
- package/components/svg/TerrawareLogoMobile.js +26 -25
- package/components/svg/Touchscreen.js +21 -19
- package/components/svg/UploadCloud.js +164 -202
- package/components/svg/Warning.js +21 -19
- package/components/svg/WelcomeClipboard.js +133 -156
- package/components/svg/index.js +129 -129
- package/components/table/EnhancedTableToolbar.js +74 -68
- package/components/table/EnhancedTableToolbarV2.js +41 -31
- package/components/table/TableCellRenderer.js +231 -196
- package/components/table/TableHeader.js +73 -71
- package/components/table/TableHeaderItem.js +102 -97
- package/components/table/density.js +19 -18
- package/components/table/index.js +369 -350
- package/components/table/sort.js +52 -27
- package/components/table/types.js +1 -0
- package/components/types/index.js +1 -0
- package/hooks/cameraEntityContext.js +13 -2
- package/hooks/useBoolean.js +13 -17
- package/hooks/useCameraPosition.js +56 -56
- package/hooks/useDevicePerformance.js +17 -17
- package/hooks/useXr.js +87 -85
- package/index.js +58 -58
- package/package.json +1 -1
- package/setupTests.js +4 -0
- package/strings/export.js +170 -111
- package/strings/index.js +54 -25
- package/strings/locales.js +41 -21
- package/strings/strings-en.js +51 -49
- package/strings/strings-es.js +51 -49
- package/strings/strings-fr.js +51 -49
- package/strings/strings-gx.js +51 -49
- package/style-dictionary-dist/TerrawareTheme.js +186 -186
- package/theme.js +76 -74
- package/utils/color.js +15 -8
- package/utils/date.js +108 -42
- package/utils/index.js +4 -4
- package/utils/preventDefaultEvent.js +3 -3
- package/utils/text.js +11 -11
- package/utils/useDeviceInfo.js +79 -42
- package/virtualWalkthrough.js +23 -23
package/strings/export.js
CHANGED
|
@@ -1,127 +1,186 @@
|
|
|
1
|
-
import { parse } from
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { parse } from 'csv-parse/sync';
|
|
2
|
+
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { resolveConfig, format } from 'prettier';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Renders the CSV string tables as TypeScript source, and finds keys in them that no source file
|
|
8
|
+
* references.
|
|
9
|
+
*
|
|
10
|
+
* This runs at build time, not at runtime. Nothing in the component runtime imports it, so its Node
|
|
11
|
+
* and CSV dependencies never reach a browser bundle. It's published rather than kept in scripts/ so
|
|
12
|
+
* that applications sharing these string tables can drive the same conversion and checks.
|
|
13
|
+
*/
|
|
5
14
|
const GENERATED_HEADER = '/* Generated from the CSV files in csv/ by `yarn generate-strings`. Do not edit. */';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Loads a CSV strings file into an object, taking keys and values from the first two columns.
|
|
18
|
+
*/
|
|
19
|
+
const csvToStrings = csvData => {
|
|
20
|
+
const rows = parse(csvData, {
|
|
21
|
+
// Skip header row
|
|
22
|
+
from: 2,
|
|
23
|
+
// Only rows for strings that have comments have 3 fields.
|
|
24
|
+
relax_column_count_less: true,
|
|
25
|
+
// Skip empty "row" that is really just the terminating linefeed
|
|
26
|
+
skip_empty_lines: true
|
|
27
|
+
});
|
|
28
|
+
return rows.reduce((result, row) => {
|
|
29
|
+
result[row[0]] = row[1];
|
|
30
|
+
return result;
|
|
31
|
+
}, {});
|
|
16
32
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Renders a strings object as a TypeScript module that exports a constant called "strings",
|
|
36
|
+
* formatted the way the repository's Prettier configuration wants it so that generating a table and
|
|
37
|
+
* formatting the tree don't fight over the result.
|
|
38
|
+
*/
|
|
39
|
+
const stringsToTypeScript = async (stringsMap, targetPath) => {
|
|
40
|
+
const source = `${GENERATED_HEADER}\nexport const strings = ${JSON.stringify(stringsMap, null, 2)};\n`;
|
|
41
|
+
const prettierConfig = await resolveConfig(targetPath);
|
|
42
|
+
return format(source, {
|
|
43
|
+
...prettierConfig,
|
|
44
|
+
filepath: targetPath
|
|
45
|
+
});
|
|
24
46
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Transforms the English strings table into gibberish, which makes untranslated text obvious
|
|
50
|
+
* without waiting on a translation.
|
|
51
|
+
*
|
|
52
|
+
* 1. Split the English string into whitespace-delimited words.
|
|
53
|
+
* 2. Reverse the order of the words.
|
|
54
|
+
* 3. Render each word as a base64 encoding of its UTF-8 representation, except for words that look
|
|
55
|
+
* like format string placeholders.
|
|
56
|
+
*/
|
|
57
|
+
const generateGibberish = english => Object.fromEntries(Object.entries(english).map(([key, value]) => [key, value.split(' ').reverse().map(word => word.startsWith('{') ? word : Buffer.from(word, 'utf-8').toString('base64').replace(/=/g, '')).join(' ')]));
|
|
58
|
+
const exportStrings = async (englishStrings, localizedStrings, locale, targetDir, defaultToEnglish) => {
|
|
59
|
+
const stringsMap = {};
|
|
60
|
+
for (const key of Object.keys(englishStrings)) {
|
|
61
|
+
if (key in localizedStrings) {
|
|
62
|
+
stringsMap[key] = localizedStrings[key];
|
|
63
|
+
} else {
|
|
64
|
+
console.warn(`Locale ${locale} has no translation for ${key}`);
|
|
65
|
+
if (defaultToEnglish) {
|
|
66
|
+
stringsMap[key] = englishStrings[key];
|
|
67
|
+
}
|
|
35
68
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
69
|
+
}
|
|
70
|
+
const targetPath = path.resolve(targetDir, `strings-${locale}.ts`);
|
|
71
|
+
await writeFile(targetPath, await stringsToTypeScript(stringsMap, targetPath), {
|
|
72
|
+
encoding: 'utf-8'
|
|
73
|
+
});
|
|
40
74
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Converts a CSV strings file to a TypeScript source file that exports a constant called "strings".
|
|
78
|
+
* This will be an object that has the same keys as the English strings file; the English strings
|
|
79
|
+
* will be used for any keys that aren't translated yet.
|
|
80
|
+
*
|
|
81
|
+
* The filename is assumed to be the locale code with a ".csv" suffix. Converting the English file
|
|
82
|
+
* also writes the gibberish table, which is derived from English rather than translated.
|
|
83
|
+
*/
|
|
84
|
+
const convertCsvFile = async (csvPath, targetDir, defaultToEnglish = true) => {
|
|
85
|
+
if (!csvPath.endsWith('.csv')) {
|
|
86
|
+
throw new Error('Cannot convert a non-CSV file');
|
|
87
|
+
}
|
|
88
|
+
const locale = path.basename(csvPath, '.csv');
|
|
89
|
+
const stringsMap = csvToStrings(await readFile(csvPath, {
|
|
90
|
+
encoding: 'utf-8'
|
|
91
|
+
}));
|
|
92
|
+
let englishStringsMap;
|
|
93
|
+
if (locale === 'en') {
|
|
94
|
+
englishStringsMap = stringsMap;
|
|
95
|
+
} else {
|
|
96
|
+
const englishPath = path.resolve(path.dirname(csvPath), 'en.csv');
|
|
97
|
+
englishStringsMap = csvToStrings(await readFile(englishPath, {
|
|
98
|
+
encoding: 'utf-8'
|
|
46
99
|
}));
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
encoding: 'utf-8'
|
|
53
|
-
}));
|
|
54
|
-
}
|
|
55
|
-
await exportStrings(englishStringsMap, stringsMap, locale, targetDir, defaultToEnglish);
|
|
56
|
-
if ('en' === locale) await exportStrings(englishStringsMap, generateGibberish(englishStringsMap), 'gx', targetDir, defaultToEnglish);
|
|
100
|
+
}
|
|
101
|
+
await exportStrings(englishStringsMap, stringsMap, locale, targetDir, defaultToEnglish);
|
|
102
|
+
if (locale === 'en') {
|
|
103
|
+
await exportStrings(englishStringsMap, generateGibberish(englishStringsMap), 'gx', targetDir, defaultToEnglish);
|
|
104
|
+
}
|
|
57
105
|
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Converts the CSV files for all locales to TypeScript source files. The list of locales is
|
|
109
|
+
* determined by the presence of CSV files.
|
|
110
|
+
*/
|
|
111
|
+
const convertAllLocales = async (csvDir, stringsDir, defaultToEnglish = true) => {
|
|
112
|
+
const csvFiles = (await readdir(csvDir)).filter(filename => filename.endsWith('.csv'));
|
|
113
|
+
await Promise.all(csvFiles.map(filename => convertCsvFile(path.join(csvDir, filename), stringsDir, defaultToEnglish)));
|
|
114
|
+
return csvFiles;
|
|
62
115
|
};
|
|
63
|
-
const DEFAULT_EXTENSIONS = [
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const entryPath = node_path.join(directory, entry.name);
|
|
78
|
-
if (entry.isDirectory()) return excludedDirectories.includes(entry.name) ? [] : findSourceFiles(entryPath, extensions, excludedDirectories);
|
|
79
|
-
return extensions.includes(node_path.extname(entry.name)) ? [
|
|
80
|
-
entryPath
|
|
81
|
-
] : [];
|
|
82
|
-
}));
|
|
83
|
-
return nested.flat().sort();
|
|
116
|
+
const DEFAULT_EXTENSIONS = ['.ts', '.js', '.tsx', '.jsx'];
|
|
117
|
+
const DEFAULT_EXCLUDED_DIRECTORIES = ['strings'];
|
|
118
|
+
const findSourceFiles = async (directory, extensions = DEFAULT_EXTENSIONS, excludedDirectories = DEFAULT_EXCLUDED_DIRECTORIES) => {
|
|
119
|
+
const entries = await readdir(directory, {
|
|
120
|
+
withFileTypes: true
|
|
121
|
+
});
|
|
122
|
+
const nested = await Promise.all(entries.map(async entry => {
|
|
123
|
+
const entryPath = path.join(directory, entry.name);
|
|
124
|
+
if (entry.isDirectory()) {
|
|
125
|
+
return excludedDirectories.includes(entry.name) ? [] : findSourceFiles(entryPath, extensions, excludedDirectories);
|
|
126
|
+
}
|
|
127
|
+
return extensions.includes(path.extname(entry.name)) ? [entryPath] : [];
|
|
128
|
+
}));
|
|
129
|
+
return nested.flat().sort();
|
|
84
130
|
};
|
|
85
131
|
const STRINGS_PROPERTY_ACCESS = /\bstrings\s*\??\.\s*(\w+)/g;
|
|
86
132
|
const STRINGS_INDEX_ACCESS = /\bstrings\s*(?:\?\.)?\s*\[([^\]]*)\]/g;
|
|
87
133
|
const QUOTED_KEY = /(['"`])(\w+)\1/g;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Returns the keys a source file reads from the strings table, either as properties
|
|
137
|
+
* (strings.SAVE) or as quoted keys inside an index expression (strings[isRead ? 'READ' : 'UNREAD']).
|
|
138
|
+
*/
|
|
139
|
+
const findAccessedKeys = source => [...Array.from(source.matchAll(STRINGS_PROPERTY_ACCESS), match => match[1]), ...Array.from(source.matchAll(STRINGS_INDEX_ACCESS)).flatMap(match => Array.from(match[1].matchAll(QUOTED_KEY), literal => literal[2]))];
|
|
140
|
+
const classifyKeys = (keys, sources) => {
|
|
141
|
+
const accessedKeys = new Set(sources.flatMap(findAccessedKeys));
|
|
142
|
+
const identifiers = new Set(sources.flatMap(source => source.match(/\w+/g) ?? []));
|
|
143
|
+
const unused = [];
|
|
144
|
+
const possiblyUnused = [];
|
|
145
|
+
keys.forEach(key => {
|
|
146
|
+
if (accessedKeys.has(key)) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (identifiers.has(key)) {
|
|
150
|
+
possiblyUnused.push(key);
|
|
151
|
+
} else {
|
|
152
|
+
unused.push(key);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
unused,
|
|
157
|
+
possiblyUnused
|
|
158
|
+
};
|
|
106
159
|
};
|
|
107
|
-
const findUnusedStrings = async ({
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
160
|
+
const findUnusedStrings = async ({
|
|
161
|
+
csvPath,
|
|
162
|
+
sourceDir,
|
|
163
|
+
extensions,
|
|
164
|
+
excludedDirectories
|
|
165
|
+
}) => {
|
|
166
|
+
const keys = Object.keys(csvToStrings(await readFile(csvPath, {
|
|
167
|
+
encoding: 'utf-8'
|
|
168
|
+
})));
|
|
169
|
+
const files = await findSourceFiles(sourceDir, extensions, excludedDirectories);
|
|
170
|
+
const sources = await Promise.all(files.map(file => readFile(file, {
|
|
171
|
+
encoding: 'utf-8'
|
|
172
|
+
})));
|
|
173
|
+
return classifyKeys(keys, sources);
|
|
116
174
|
};
|
|
117
|
-
const formatUnusedStrings = ({
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
175
|
+
const formatUnusedStrings = ({
|
|
176
|
+
unused,
|
|
177
|
+
possiblyUnused
|
|
178
|
+
}) => {
|
|
179
|
+
const lines = unused.length === 0 ? ['No unused entries found.'] : ['Unused entries found:', ...unused];
|
|
180
|
+
if (possiblyUnused.length > 0) {
|
|
181
|
+
lines.push('', 'Possibly unused entries (never read from strings, but the name appears elsewhere; check manually):', ...possiblyUnused);
|
|
182
|
+
}
|
|
183
|
+
return `${lines.join('\n')}\n`;
|
|
126
184
|
};
|
|
185
|
+
|
|
127
186
|
export { classifyKeys, convertAllLocales, convertCsvFile, csvToStrings, findAccessedKeys, findSourceFiles, findUnusedStrings, formatUnusedStrings, generateGibberish, stringsToTypeScript };
|
package/strings/index.js
CHANGED
|
@@ -1,33 +1,62 @@
|
|
|
1
|
-
import { useSyncExternalStore } from
|
|
2
|
-
import { findLocaleDetails, supportedLocales } from
|
|
3
|
-
import { strings } from
|
|
4
|
-
import { strings as
|
|
5
|
-
import { strings as
|
|
6
|
-
import { strings as
|
|
1
|
+
import { useSyncExternalStore } from 'react';
|
|
2
|
+
import { findLocaleDetails, supportedLocales } from './locales.js';
|
|
3
|
+
import { strings } from './strings-en.js';
|
|
4
|
+
import { strings as strings$3 } from './strings-es.js';
|
|
5
|
+
import { strings as strings$2 } from './strings-fr.js';
|
|
6
|
+
import { strings as strings$1 } from './strings-gx.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The set of strings every locale provides. The English table is the source of truth for the list
|
|
10
|
+
* of keys.
|
|
11
|
+
*/
|
|
12
|
+
|
|
7
13
|
const tables = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
en: strings,
|
|
15
|
+
es: strings$3,
|
|
16
|
+
fr: strings$2,
|
|
17
|
+
gx: strings$1
|
|
12
18
|
};
|
|
13
|
-
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Locales we have a bundled string table for. A locale can be supported -- listed in a locale
|
|
22
|
+
* selector -- before its table lands, and resolving against the tables we actually have keeps
|
|
23
|
+
* `getLocale` and `getStrings` describing the same language.
|
|
24
|
+
*/
|
|
25
|
+
const localesWithTables = () => supportedLocales.filter(locale => locale.id in tables);
|
|
14
26
|
let currentLocale = 'en';
|
|
15
27
|
let currentStrings = strings;
|
|
16
28
|
const listeners = new Set();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Sets the locale these components render in. Consumers own the locale -- a component library has
|
|
32
|
+
* no reliable way to know whether the browser's language or a user's profile setting should win --
|
|
33
|
+
* so nothing here changes until an application calls this. Accepts a bare language tag or one with
|
|
34
|
+
* a region ('es' or 'es-MX').
|
|
35
|
+
*/
|
|
36
|
+
const setLocale = locale => {
|
|
37
|
+
const resolved = findLocaleDetails(localesWithTables(), locale).id;
|
|
38
|
+
if (resolved === currentLocale) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
currentLocale = resolved;
|
|
42
|
+
currentStrings = tables[resolved] ?? strings;
|
|
43
|
+
listeners.forEach(notify => notify());
|
|
23
44
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
|
|
46
|
+
/** The resolved locale, which is also the locale to hand to Intl and other formatters. */
|
|
47
|
+
const getLocale = () => currentLocale;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The current string table. For components, prefer `useStrings`, which re-renders on a locale
|
|
51
|
+
* change; this is for the imperative code that can't hold a hook, such as PlayCanvas scripts.
|
|
52
|
+
*/
|
|
53
|
+
const getStrings = () => currentStrings;
|
|
54
|
+
const subscribe = notify => {
|
|
55
|
+
listeners.add(notify);
|
|
56
|
+
return () => {
|
|
57
|
+
listeners.delete(notify);
|
|
58
|
+
};
|
|
31
59
|
};
|
|
32
|
-
const useStrings = ()=>useSyncExternalStore(subscribe, getStrings, getStrings);
|
|
60
|
+
const useStrings = () => useSyncExternalStore(subscribe, getStrings, getStrings);
|
|
61
|
+
|
|
33
62
|
export { getLocale, getStrings, setLocale, useStrings };
|
package/strings/locales.js
CHANGED
|
@@ -1,22 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
1
|
+
/** Supported locales in the order they should appear in a locale selector. */
|
|
2
|
+
const supportedLocales = [{
|
|
3
|
+
id: 'en',
|
|
4
|
+
name: 'English'
|
|
5
|
+
}, {
|
|
6
|
+
id: 'es',
|
|
7
|
+
name: 'Español'
|
|
8
|
+
}, {
|
|
9
|
+
id: 'fr',
|
|
10
|
+
name: 'Français'
|
|
11
|
+
}, {
|
|
12
|
+
id: 'gx',
|
|
13
|
+
name: 'Gibberish',
|
|
14
|
+
inDevelopment: true
|
|
15
|
+
}];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns the locales that should be offered to users. Locales still in development are left out
|
|
19
|
+
* unless they're asked for, which is how a consumer exposes them outside of production.
|
|
20
|
+
*/
|
|
21
|
+
const getSupportedLocales = (includeInDevelopment = false) => includeInDevelopment ? supportedLocales : supportedLocales.filter(locale => !locale.inDevelopment);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns the locale from a list of locales that matches the user's selected language.
|
|
25
|
+
*
|
|
26
|
+
* The user's locale can include both a language and a country code, but entries in a list of
|
|
27
|
+
* languages usually don't have country codes. So we need to find the entry that is the longest
|
|
28
|
+
* prefix of the user's locale.
|
|
29
|
+
*
|
|
30
|
+
* If the locale is es-MX and the list of locales only has es, we want the es item to be selected.
|
|
31
|
+
* But if the locale is zh-CN and the list has both zh-CN and zh-TW, we want zh-CN to be selected.
|
|
32
|
+
*
|
|
33
|
+
* If none of the locales in the list matches, returns the first locale from the list. The list
|
|
34
|
+
* must not be empty.
|
|
35
|
+
*/
|
|
36
|
+
const findLocaleDetails = (locales, locale) => locales.reduce((bestMatch, candidate) =>
|
|
37
|
+
// The best match so far is only worth keeping if it's a prefix of the requested locale too.
|
|
38
|
+
// reduce() seeds with the first locale without checking it, so a leading 'en-US' would
|
|
39
|
+
// otherwise be treated as a match for 'en' and never replaced.
|
|
40
|
+
locale.startsWith(candidate.id) && (!locale.startsWith(bestMatch.id) || candidate.id.length > bestMatch.id.length) ? candidate : bestMatch);
|
|
41
|
+
|
|
22
42
|
export { findLocaleDetails, getSupportedLocales, supportedLocales };
|
package/strings/strings-en.js
CHANGED
|
@@ -1,52 +1,54 @@
|
|
|
1
|
+
/* Generated from the CSV files in csv/ by `yarn generate-strings`. Do not edit. */
|
|
1
2
|
const strings = {
|
|
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
|
-
|
|
3
|
+
ADD_ANNOTATION: 'Add Annotation',
|
|
4
|
+
ANNOTATION_DESCRIPTION_TOOLTIP: 'Additional details or context about this annotation',
|
|
5
|
+
ANNOTATION_LABEL_TOOLTIP: 'Short identifier displayed on the annotation hotspot',
|
|
6
|
+
ANNOTATION_TITLE_TOOLTIP: 'Main heading for the annotation',
|
|
7
|
+
ANNOTATIONS: 'Annotations',
|
|
8
|
+
AR: 'AR',
|
|
9
|
+
ARROW_KEYS: 'Arrow Keys',
|
|
10
|
+
AUTO_ROTATE: 'Auto Rotate',
|
|
11
|
+
BOUNDED_FLY: 'Bounded-fly',
|
|
12
|
+
CAMERA_FOCUS_POINT: 'Focus Point',
|
|
13
|
+
CAMERA_INFO: 'Camera Info',
|
|
14
|
+
CAMERA_POSITION: 'Position',
|
|
15
|
+
CANCEL: 'Cancel',
|
|
16
|
+
CHOOSE_FILE: 'Choose File...',
|
|
17
|
+
CONTROLS: 'Controls',
|
|
18
|
+
CTRL: 'Ctrl',
|
|
19
|
+
DELETE_ANNOTATION: 'Delete Annotation',
|
|
20
|
+
DESCRIPTION: 'Description',
|
|
21
|
+
DESELECT_ANNOTATION: 'Deselect Annotation',
|
|
22
|
+
EDIT: 'Edit',
|
|
23
|
+
EDIT_ANNOTATION: 'Edit Annotation',
|
|
24
|
+
EXISTING: 'Existing',
|
|
25
|
+
FILE_SELECTED: 'File Selected',
|
|
26
|
+
FLY: 'Fly',
|
|
27
|
+
FLY_FASTER: 'Fly faster',
|
|
28
|
+
FLY_SLOWER: 'Fly slower',
|
|
29
|
+
FREE_FLY: 'Free-fly',
|
|
30
|
+
IMAGES: 'Images',
|
|
31
|
+
LABEL: 'Label',
|
|
32
|
+
LEFT_MOUSE: 'Left Mouse',
|
|
33
|
+
LOOK: 'Look',
|
|
34
|
+
MIDDLE_MOUSE: 'Middle Mouse',
|
|
35
|
+
MOUSE_WHEEL: 'Mouse Wheel',
|
|
36
|
+
NEW: 'New',
|
|
37
|
+
ORBIT: 'Orbit',
|
|
38
|
+
PAN: 'Pan',
|
|
39
|
+
PINCH: 'Pinch',
|
|
40
|
+
REPLACE_FILE: 'Replace File...',
|
|
41
|
+
RESET_CAMERA: 'Reset Camera',
|
|
42
|
+
RIGHT_MOUSE: 'Right Mouse',
|
|
43
|
+
SAVE: 'Save',
|
|
44
|
+
SHIFT: 'Shift',
|
|
45
|
+
SWIPE: 'Swipe',
|
|
46
|
+
TITLE: 'Title',
|
|
47
|
+
TOUCH_DRAG: 'Touch + Drag',
|
|
48
|
+
UPLOAD_FILES: 'Upload Files',
|
|
49
|
+
UPLOAD_FILES_DESCRIPTION: 'Browse or drag and drop files.',
|
|
50
|
+
VR: 'VR',
|
|
51
|
+
ZOOM: 'Zoom'
|
|
51
52
|
};
|
|
53
|
+
|
|
52
54
|
export { strings };
|