translate-shield 0.1.0 → 0.1.1
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/README.md +56 -5
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -25,10 +25,17 @@ initTranslateShield()
|
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
The whole setup. No-op on the server, armed only after a translator rewrites the page, inert on
|
|
28
|
-
engines that do not need it. No dependencies, about
|
|
28
|
+
engines that do not need it. No dependencies, about 15 kB packed.
|
|
29
29
|
|
|
30
30
|
Firefox and Edge readers do not hit this bug. Check [Browser support](#browser-support) first.
|
|
31
31
|
|
|
32
|
+
## See it happen
|
|
33
|
+
|
|
34
|
+
[Live demo](https://google-translate-simulation.netlify.app/). Two identical React apps side by
|
|
35
|
+
side, one shielded and one not, while your own browser translates them. Turn translation on and the
|
|
36
|
+
unprotected one freezes and then unmounts. It runs on real translation rather than a recording, so
|
|
37
|
+
on Edge and Firefox both panels correctly behave the same and the page says so.
|
|
38
|
+
|
|
32
39
|
## What the reader sees
|
|
33
40
|
|
|
34
41
|
Four updates to a value in view, real Chrome, live Dutch translation.
|
|
@@ -38,12 +45,13 @@ Four updates to a value in view, real Chrome, live Dutch translation.
|
|
|
38
45
|
| `NotFoundError` crash | app unmounts | fixed | fixed | fixed |
|
|
39
46
|
| Value keeps updating | frozen | still frozen | yes | yes |
|
|
40
47
|
| Removed text disappears | n/a | stays on screen | yes | yes |
|
|
41
|
-
| Language while updating | n/a | n/a | 150
|
|
48
|
+
| Language while updating | n/a | n/a | 100-150 ms of source language per update | 0 ms |
|
|
42
49
|
| Text when the reader looks | `Er zijn 4 lampen!`, stale | n/a | `There are 7 lights!` | `Er zijn 7 lampen!` |
|
|
43
50
|
|
|
44
|
-
First three rows: `research/comparison.json`. Language row: `research/flicker.json`,
|
|
45
|
-
restore-and-retranslate spends
|
|
46
|
-
row:
|
|
51
|
+
First three rows: `research/comparison.json`. Language row: `research/flicker.json`, five
|
|
52
|
+
replicates of four updates, where restore-and-retranslate spends 500 to 600 ms of the sequence in
|
|
53
|
+
source language and mirroring 0 ms in all twenty. Last row:
|
|
54
|
+
`research/head-to-head-real-chrome.json`.
|
|
47
55
|
|
|
48
56
|
## Next.js
|
|
49
57
|
|
|
@@ -79,6 +87,49 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
79
87
|
A second call returns the existing handle, so a double-invoked development effect leaves one
|
|
80
88
|
shield.
|
|
81
89
|
|
|
90
|
+
### The hydration warning is not this library
|
|
91
|
+
|
|
92
|
+
A translated page produces this before any of the above runs:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.
|
|
96
|
+
- lang="ru"
|
|
97
|
+
- className="... translated-ltr"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The translator rewrites the document root and a set of text-carrying attributes while the page is
|
|
101
|
+
still loading, so React hydrates against a DOM the server never sent.
|
|
102
|
+
|
|
103
|
+
| Surface | What the translator does | Evidence |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `<html lang>` | rewritten to the target language, on every engine recorded | `research/root-attributes.json`, `research/fingerprints/` |
|
|
106
|
+
| `<html class>` | Chrome appends `translated-ltr` | `research/root-attributes.json` |
|
|
107
|
+
| `alt`, `title`, `placeholder`, `aria-label`, submit `value` | translated in place | `research/attributes.json` |
|
|
108
|
+
| `data-*` attributes, `meta[name=description]` | left alone | `research/attributes.json` |
|
|
109
|
+
|
|
110
|
+
Nothing here causes it and nothing here can prevent it: it happens before hydration, and
|
|
111
|
+
`initTranslateShield` runs after. React keeps the translator's value either way, so this is a
|
|
112
|
+
warning about the console, not a broken page. Two fixes, depending on what the attribute holds.
|
|
113
|
+
|
|
114
|
+
Text that should be translated, which is most `alt` and `aria-label` copy:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
<html lang="en" suppressHydrationWarning>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`suppressHydrationWarning` covers one element's own attributes, so the root needs it for `lang` and
|
|
121
|
+
`class`, and any element whose `alt` or `title` you want translated needs its own.
|
|
122
|
+
|
|
123
|
+
Text that should not be translated, such as a code, a price or an order number, opts out instead,
|
|
124
|
+
and the opt-out covers the attribute as well as the text:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<img translate="no" className="notranslate" alt="Order PIN-4417-02" />
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
All three opt-out probes held on Chrome (`research/attributes.json`). That is the same guarantee
|
|
131
|
+
`NoTranslate` relies on.
|
|
132
|
+
|
|
82
133
|
## Protecting a value
|
|
83
134
|
|
|
84
135
|
```tsx
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "translate-shield",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Keeps React and Next.js apps correct when a browser translator rewrites the DOM: no crash, no frozen values, and no flash of the original language.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"google-translate",
|
|
@@ -69,7 +69,9 @@
|
|
|
69
69
|
"verify": "npm run typecheck && npm run build && npm run smoke && npx publint --strict && npx @arethetypeswrong/cli --pack .",
|
|
70
70
|
"prepublishOnly": "npm run typecheck && npm run build && npm run smoke && npx publint --strict",
|
|
71
71
|
"postbuild": "node scripts/assert-directives.mjs",
|
|
72
|
-
"smoke": "node scripts/smoke.mjs"
|
|
72
|
+
"smoke": "node scripts/smoke.mjs",
|
|
73
|
+
"demo": "npm run dev -w demo",
|
|
74
|
+
"build:demo": "npm run build -w demo"
|
|
73
75
|
},
|
|
74
76
|
"devDependencies": {
|
|
75
77
|
"@arethetypeswrong/cli": "^0.18.0",
|
|
@@ -85,7 +87,8 @@
|
|
|
85
87
|
"vitest": "^2.1.0"
|
|
86
88
|
},
|
|
87
89
|
"workspaces": [
|
|
88
|
-
"tests/app"
|
|
90
|
+
"tests/app",
|
|
91
|
+
"demo"
|
|
89
92
|
],
|
|
90
93
|
"engines": {
|
|
91
94
|
"node": ">=18"
|