@jobber/components-native 0.112.1 → 0.113.0
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/dist/package.json +4 -3
- package/dist/src/hooks/useAtlantisI18n/useAtlantisI18n.js +2 -4
- package/dist/src/hooks/useAtlantisI18n/useAtlantisI18n.test.js +15 -115
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/hooks/useAtlantisI18n/useAtlantisI18n.d.ts +1 -3
- package/package.json +4 -3
- package/src/hooks/useAtlantisI18n/useAtlantisI18n.test.ts +25 -202
- package/src/hooks/useAtlantisI18n/useAtlantisI18n.ts +3 -13
- package/dist/src/hooks/useAtlantisI18n/releasedLanguages.js +0 -21
- package/dist/types/src/hooks/useAtlantisI18n/releasedLanguages.d.ts +0 -18
- package/src/hooks/useAtlantisI18n/releasedLanguages.ts +0 -23
|
@@ -2,9 +2,7 @@ import en from "./locales/en.json";
|
|
|
2
2
|
export type I18nKeys = keyof typeof en;
|
|
3
3
|
export interface useAtlantisI18nValue {
|
|
4
4
|
/**
|
|
5
|
-
* The
|
|
6
|
-
* list of released languages. Regional variants (e.g. "en-CA") are preserved
|
|
7
|
-
* when their language is released. Unreleased locales fall back to "en".
|
|
5
|
+
* The set locale based on the AtlantisContext.
|
|
8
6
|
*/
|
|
9
7
|
readonly locale: string;
|
|
10
8
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.113.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React Native implementation of Atlantis",
|
|
6
6
|
"repository": {
|
|
@@ -53,7 +53,8 @@
|
|
|
53
53
|
"compile": "tsc -p tsconfig.build.json",
|
|
54
54
|
"build:clean": "rm -rf ./dist",
|
|
55
55
|
"storybook": "storybook dev -p 6008 --disable-telemetry",
|
|
56
|
-
"storybook:build": "storybook build --disable-telemetry"
|
|
56
|
+
"storybook:build": "storybook build --disable-telemetry",
|
|
57
|
+
"lint:locales": "node scripts/check-locale-parity.mjs"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
60
|
"@react-native-clipboard/clipboard": "^1.11.2",
|
|
@@ -124,5 +125,5 @@
|
|
|
124
125
|
"react-native-screens": ">=4.18.0",
|
|
125
126
|
"react-native-svg": ">=12.0.0"
|
|
126
127
|
},
|
|
127
|
-
"gitHead": "
|
|
128
|
+
"gitHead": "25cd8815738e86b9f49311096e7d4ffd02d843b5"
|
|
128
129
|
}
|
|
@@ -2,7 +2,6 @@ import { renderHook } from "@testing-library/react-native";
|
|
|
2
2
|
import { useAtlantisI18n } from ".";
|
|
3
3
|
import en from "./locales/en.json";
|
|
4
4
|
import es from "./locales/es.json";
|
|
5
|
-
import * as releasedLanguages from "./releasedLanguages";
|
|
6
5
|
import * as context from "../../AtlantisContext";
|
|
7
6
|
|
|
8
7
|
jest.mock("../../AtlantisContext", () => ({
|
|
@@ -11,14 +10,6 @@ jest.mock("../../AtlantisContext", () => ({
|
|
|
11
10
|
...jest.requireActual("../../AtlantisContext"),
|
|
12
11
|
}));
|
|
13
12
|
|
|
14
|
-
// Mock releasedLanguages so that jest.spyOn can reliably intercept calls from
|
|
15
|
-
// inside the hook. Without this, Babel may resolve the named import as a local
|
|
16
|
-
// binding at module load time, bypassing any spy set up afterwards.
|
|
17
|
-
jest.mock("./releasedLanguages", () => ({
|
|
18
|
-
__esModule: true,
|
|
19
|
-
...jest.requireActual("./releasedLanguages"),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
13
|
const spy = jest.spyOn(context, "useAtlantisContext");
|
|
23
14
|
const testDate = new Date("2020-01-01T00:00:00.000Z");
|
|
24
15
|
const dateAfterSpringForward = new Date("2020-04-10T00:00:00.000Z");
|
|
@@ -43,21 +34,7 @@ describe("useAtlantisI18n", () => {
|
|
|
43
34
|
);
|
|
44
35
|
});
|
|
45
36
|
|
|
46
|
-
// These tests bypass the release gate via a spy so we can validate Spanish
|
|
47
|
-
// string correctness as the feature is built, independently of RELEASED_LANGUAGES.
|
|
48
37
|
describe("Español", () => {
|
|
49
|
-
let getReleasedLocaleSpy: jest.SpyInstance;
|
|
50
|
-
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
getReleasedLocaleSpy = jest
|
|
53
|
-
.spyOn(releasedLanguages, "getReleasedLocale")
|
|
54
|
-
.mockImplementation(locale => locale);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
afterEach(() => {
|
|
58
|
-
getReleasedLocaleSpy.mockRestore();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
38
|
it("should return español", () => {
|
|
62
39
|
spy.mockReturnValueOnce({
|
|
63
40
|
...context.atlantisContextDefaultValues,
|
|
@@ -67,191 +44,17 @@ describe("useAtlantisI18n", () => {
|
|
|
67
44
|
|
|
68
45
|
expect(result.current.t("cancel")).toBe("Cancelar");
|
|
69
46
|
});
|
|
47
|
+
});
|
|
70
48
|
|
|
71
|
-
|
|
49
|
+
describe("Unsupported language", () => {
|
|
50
|
+
it("should return the english translation", () => {
|
|
72
51
|
spy.mockReturnValueOnce({
|
|
73
52
|
...context.atlantisContextDefaultValues,
|
|
74
|
-
locale: "
|
|
53
|
+
locale: "fr",
|
|
75
54
|
});
|
|
76
55
|
const { result } = renderHook(useAtlantisI18n);
|
|
77
56
|
|
|
78
|
-
expect(result.current.t("cancel")).toBe("
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe("formatDate", () => {
|
|
82
|
-
it("should return the date formatted for es", () => {
|
|
83
|
-
spy.mockReturnValueOnce({
|
|
84
|
-
...context.atlantisContextDefaultValues,
|
|
85
|
-
locale: "es",
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
89
|
-
expect(result.current.formatDate(testDate)).toBe("1 ene 2020");
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe("formatTime", () => {
|
|
94
|
-
it("should return the time formatted for es", () => {
|
|
95
|
-
spy.mockReturnValueOnce({
|
|
96
|
-
...context.atlantisContextDefaultValues,
|
|
97
|
-
locale: "es",
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
101
|
-
expect(result.current.formatTime(testDate)).toBe("00:00");
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe("RELEASED_LANGUAGES fallback", () => {
|
|
107
|
-
describe("unsupported language (fr)", () => {
|
|
108
|
-
it("should return 'en' as the locale", () => {
|
|
109
|
-
spy.mockReturnValueOnce({
|
|
110
|
-
...context.atlantisContextDefaultValues,
|
|
111
|
-
locale: "fr",
|
|
112
|
-
});
|
|
113
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
114
|
-
|
|
115
|
-
expect(result.current.locale).toBe("en");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("should return english translations", () => {
|
|
119
|
-
spy.mockReturnValueOnce({
|
|
120
|
-
...context.atlantisContextDefaultValues,
|
|
121
|
-
locale: "fr",
|
|
122
|
-
});
|
|
123
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
124
|
-
|
|
125
|
-
expect(result.current.t("cancel")).toBe("Cancel");
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe("es-US (unreleased locale variant)", () => {
|
|
130
|
-
it("should return 'en' as the locale", () => {
|
|
131
|
-
spy.mockReturnValueOnce({
|
|
132
|
-
...context.atlantisContextDefaultValues,
|
|
133
|
-
locale: "es-US",
|
|
134
|
-
});
|
|
135
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
136
|
-
|
|
137
|
-
expect(result.current.locale).toBe("en");
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("should return english translations", () => {
|
|
141
|
-
spy.mockReturnValueOnce({
|
|
142
|
-
...context.atlantisContextDefaultValues,
|
|
143
|
-
locale: "es-US",
|
|
144
|
-
});
|
|
145
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
146
|
-
|
|
147
|
-
expect(result.current.t("cancel")).toBe("Cancel");
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("should format date using english locale", () => {
|
|
151
|
-
spy.mockReturnValueOnce({
|
|
152
|
-
...context.atlantisContextDefaultValues,
|
|
153
|
-
locale: "es-US",
|
|
154
|
-
});
|
|
155
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
156
|
-
|
|
157
|
-
expect(result.current.formatDate(testDate)).toBe("Jan 1, 2020");
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("should format time using english locale", () => {
|
|
161
|
-
spy.mockReturnValueOnce({
|
|
162
|
-
...context.atlantisContextDefaultValues,
|
|
163
|
-
locale: "es-US",
|
|
164
|
-
});
|
|
165
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
166
|
-
|
|
167
|
-
expect(result.current.formatTime(testDate)).toBe("12:00 AM");
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
describe("es (not yet released)", () => {
|
|
172
|
-
it("should return 'en' as the locale", () => {
|
|
173
|
-
spy.mockReturnValueOnce({
|
|
174
|
-
...context.atlantisContextDefaultValues,
|
|
175
|
-
locale: "es",
|
|
176
|
-
});
|
|
177
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
178
|
-
|
|
179
|
-
expect(result.current.locale).toBe("en");
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("should return english translations", () => {
|
|
183
|
-
spy.mockReturnValueOnce({
|
|
184
|
-
...context.atlantisContextDefaultValues,
|
|
185
|
-
locale: "es",
|
|
186
|
-
});
|
|
187
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
188
|
-
|
|
189
|
-
expect(result.current.t("cancel")).toBe("Cancel");
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("should format date using english locale", () => {
|
|
193
|
-
spy.mockReturnValueOnce({
|
|
194
|
-
...context.atlantisContextDefaultValues,
|
|
195
|
-
locale: "es",
|
|
196
|
-
});
|
|
197
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
198
|
-
|
|
199
|
-
expect(result.current.formatDate(testDate)).toBe("Jan 1, 2020");
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("should format time using english locale", () => {
|
|
203
|
-
spy.mockReturnValueOnce({
|
|
204
|
-
...context.atlantisContextDefaultValues,
|
|
205
|
-
locale: "es",
|
|
206
|
-
});
|
|
207
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
208
|
-
|
|
209
|
-
expect(result.current.formatTime(testDate)).toBe("12:00 AM");
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
describe("en-CA (released language, regional variant)", () => {
|
|
214
|
-
it("should preserve the full locale for date formatting", () => {
|
|
215
|
-
spy.mockReturnValueOnce({
|
|
216
|
-
...context.atlantisContextDefaultValues,
|
|
217
|
-
locale: "en-CA",
|
|
218
|
-
});
|
|
219
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
220
|
-
|
|
221
|
-
expect(result.current.locale).toBe("en-CA");
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it("should return english translations", () => {
|
|
225
|
-
spy.mockReturnValueOnce({
|
|
226
|
-
...context.atlantisContextDefaultValues,
|
|
227
|
-
locale: "en-CA",
|
|
228
|
-
});
|
|
229
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
230
|
-
|
|
231
|
-
expect(result.current.t("cancel")).toBe("Cancel");
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe("en-GB (released language, regional variant)", () => {
|
|
236
|
-
it("should preserve the full locale for date formatting", () => {
|
|
237
|
-
spy.mockReturnValueOnce({
|
|
238
|
-
...context.atlantisContextDefaultValues,
|
|
239
|
-
locale: "en-GB",
|
|
240
|
-
});
|
|
241
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
242
|
-
|
|
243
|
-
expect(result.current.locale).toBe("en-GB");
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it("should return english translations", () => {
|
|
247
|
-
spy.mockReturnValueOnce({
|
|
248
|
-
...context.atlantisContextDefaultValues,
|
|
249
|
-
locale: "en-GB",
|
|
250
|
-
});
|
|
251
|
-
const { result } = renderHook(useAtlantisI18n);
|
|
252
|
-
|
|
253
|
-
expect(result.current.t("cancel")).toBe("Cancel");
|
|
254
|
-
});
|
|
57
|
+
expect(result.current.t("cancel")).toBe("Cancel");
|
|
255
58
|
});
|
|
256
59
|
});
|
|
257
60
|
|
|
@@ -267,6 +70,16 @@ describe("useAtlantisI18n", () => {
|
|
|
267
70
|
expect(result.current.formatDate(testDate)).toBe("Jan 1, 2020");
|
|
268
71
|
});
|
|
269
72
|
|
|
73
|
+
it("should return the date formatted for es", () => {
|
|
74
|
+
spy.mockReturnValueOnce({
|
|
75
|
+
...context.atlantisContextDefaultValues,
|
|
76
|
+
locale: "es",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const { result } = renderHook(useAtlantisI18n);
|
|
80
|
+
expect(result.current.formatDate(testDate)).toBe("1 ene 2020");
|
|
81
|
+
});
|
|
82
|
+
|
|
270
83
|
describe("Timezone", () => {
|
|
271
84
|
it.each([
|
|
272
85
|
["America/New_York", "Dec 31, 2019"],
|
|
@@ -292,6 +105,16 @@ describe("useAtlantisI18n", () => {
|
|
|
292
105
|
expect(result.current.formatTime(testDate)).toBe("12:00 AM");
|
|
293
106
|
});
|
|
294
107
|
|
|
108
|
+
it("should return the time formatted for es", () => {
|
|
109
|
+
spy.mockReturnValueOnce({
|
|
110
|
+
...context.atlantisContextDefaultValues,
|
|
111
|
+
locale: "es",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const { result } = renderHook(useAtlantisI18n);
|
|
115
|
+
expect(result.current.formatTime(testDate)).toBe("00:00");
|
|
116
|
+
});
|
|
117
|
+
|
|
295
118
|
describe("Timezone", () => {
|
|
296
119
|
it.each([
|
|
297
120
|
["America/New_York", "7:00 PM"],
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import en from "./locales/en.json";
|
|
3
3
|
import es from "./locales/es.json";
|
|
4
|
-
import { getReleasedLocale } from "./releasedLanguages";
|
|
5
4
|
import { dateFormatter } from "./utils/dateFormatter";
|
|
6
5
|
import { useAtlantisContext } from "../../AtlantisContext";
|
|
7
6
|
|
|
@@ -9,9 +8,7 @@ export type I18nKeys = keyof typeof en;
|
|
|
9
8
|
|
|
10
9
|
export interface useAtlantisI18nValue {
|
|
11
10
|
/**
|
|
12
|
-
* The
|
|
13
|
-
* list of released languages. Regional variants (e.g. "en-CA") are preserved
|
|
14
|
-
* when their language is released. Unreleased locales fall back to "en".
|
|
11
|
+
* The set locale based on the AtlantisContext.
|
|
15
12
|
*/
|
|
16
13
|
readonly locale: string;
|
|
17
14
|
|
|
@@ -35,14 +32,7 @@ export interface useAtlantisI18nValue {
|
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
export function useAtlantisI18n(): useAtlantisI18nValue {
|
|
38
|
-
const {
|
|
39
|
-
locale: contextLocale,
|
|
40
|
-
dateFormat,
|
|
41
|
-
timeFormat,
|
|
42
|
-
timeZone,
|
|
43
|
-
} = useAtlantisContext();
|
|
44
|
-
|
|
45
|
-
const locale = getReleasedLocale(contextLocale);
|
|
35
|
+
const { locale, dateFormat, timeFormat, timeZone } = useAtlantisContext();
|
|
46
36
|
|
|
47
37
|
const t = useCallback(
|
|
48
38
|
(messageKey: keyof typeof en, values?: Record<string, string>) =>
|
|
@@ -64,7 +54,7 @@ export function useAtlantisI18n(): useAtlantisI18nValue {
|
|
|
64
54
|
}
|
|
65
55
|
|
|
66
56
|
function getLocalizedStrings(locale: string): typeof en {
|
|
67
|
-
switch (locale
|
|
57
|
+
switch (locale) {
|
|
68
58
|
case "es":
|
|
69
59
|
return es;
|
|
70
60
|
default:
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Languages that have been fully released and are safe to surface to users.
|
|
3
|
-
* Any locale not present in this list will fall back to "en".
|
|
4
|
-
*
|
|
5
|
-
* To release a new language, add its BCP 47 language tag here (e.g. "es").
|
|
6
|
-
*/
|
|
7
|
-
export const RELEASED_LANGUAGES = ["en"];
|
|
8
|
-
/**
|
|
9
|
-
* Returns the locale to use based on what has been released. Matches on the
|
|
10
|
-
* language part of the locale (before the "-") so that regional variants like
|
|
11
|
-
* "en-CA" or "es-US" resolve correctly:
|
|
12
|
-
*
|
|
13
|
-
* - "en-CA" → "en-CA" (language "en" is released; full locale preserved for
|
|
14
|
-
* date/time formatting)
|
|
15
|
-
* - "es-US" → "en" (language "es" not yet released; falls back to "en")
|
|
16
|
-
* - "fr" → "en" (language "fr" not released; falls back to "en")
|
|
17
|
-
*/
|
|
18
|
-
export function getReleasedLocale(locale) {
|
|
19
|
-
const language = locale.split("-")[0];
|
|
20
|
-
return RELEASED_LANGUAGES.includes(language) ? locale : "en";
|
|
21
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Languages that have been fully released and are safe to surface to users.
|
|
3
|
-
* Any locale not present in this list will fall back to "en".
|
|
4
|
-
*
|
|
5
|
-
* To release a new language, add its BCP 47 language tag here (e.g. "es").
|
|
6
|
-
*/
|
|
7
|
-
export declare const RELEASED_LANGUAGES: readonly string[];
|
|
8
|
-
/**
|
|
9
|
-
* Returns the locale to use based on what has been released. Matches on the
|
|
10
|
-
* language part of the locale (before the "-") so that regional variants like
|
|
11
|
-
* "en-CA" or "es-US" resolve correctly:
|
|
12
|
-
*
|
|
13
|
-
* - "en-CA" → "en-CA" (language "en" is released; full locale preserved for
|
|
14
|
-
* date/time formatting)
|
|
15
|
-
* - "es-US" → "en" (language "es" not yet released; falls back to "en")
|
|
16
|
-
* - "fr" → "en" (language "fr" not released; falls back to "en")
|
|
17
|
-
*/
|
|
18
|
-
export declare function getReleasedLocale(locale: string): string;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Languages that have been fully released and are safe to surface to users.
|
|
3
|
-
* Any locale not present in this list will fall back to "en".
|
|
4
|
-
*
|
|
5
|
-
* To release a new language, add its BCP 47 language tag here (e.g. "es").
|
|
6
|
-
*/
|
|
7
|
-
export const RELEASED_LANGUAGES: readonly string[] = ["en"];
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Returns the locale to use based on what has been released. Matches on the
|
|
11
|
-
* language part of the locale (before the "-") so that regional variants like
|
|
12
|
-
* "en-CA" or "es-US" resolve correctly:
|
|
13
|
-
*
|
|
14
|
-
* - "en-CA" → "en-CA" (language "en" is released; full locale preserved for
|
|
15
|
-
* date/time formatting)
|
|
16
|
-
* - "es-US" → "en" (language "es" not yet released; falls back to "en")
|
|
17
|
-
* - "fr" → "en" (language "fr" not released; falls back to "en")
|
|
18
|
-
*/
|
|
19
|
-
export function getReleasedLocale(locale: string): string {
|
|
20
|
-
const language = locale.split("-")[0];
|
|
21
|
-
|
|
22
|
-
return RELEASED_LANGUAGES.includes(language) ? locale : "en";
|
|
23
|
-
}
|