Skip to content

Commit 65ccea6

Browse files
jhnnssindresorhus
andauthored
Add option to disable the platform locale (#92)
Co-authored-by: Sindre Sorhus <[email protected]>
1 parent f28c463 commit 65ccea6

File tree

4 files changed

+75
-17
lines changed

4 files changed

+75
-17
lines changed

index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ declare namespace camelcase {
1717
/**
1818
The locale parameter indicates the locale to be used to convert to upper/lower case according to any locale-specific case mappings. If multiple locales are given in an array, the best available locale is used.
1919
20+
Setting `locale: false` ignores the platform locale and uses the [Unicode Default Case Conversion](https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#simple-single-character-case-mapping) algorithm.
21+
2022
Default: The host environment’s current locale.
2123
2224
@example
@@ -33,7 +35,7 @@ declare namespace camelcase {
3335
//=> 'loremİpsum'
3436
```
3537
*/
36-
readonly locale?: string | readonly string[];
38+
readonly locale?: false | string | readonly string[];
3739
}
3840
}
3941

index.js

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
1010
const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
1111
const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu');
1212

13-
const preserveCamelCase = (string, locale) => {
13+
const preserveCamelCase = (string, toLowerCase, toUpperCase) => {
1414
let isLastCharLower = false;
1515
let isLastCharUpper = false;
1616
let isLastLastCharUpper = false;
@@ -30,27 +30,27 @@ const preserveCamelCase = (string, locale) => {
3030
isLastCharUpper = false;
3131
isLastCharLower = true;
3232
} else {
33-
isLastCharLower = character.toLocaleLowerCase(locale) === character && character.toLocaleUpperCase(locale) !== character;
33+
isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
3434
isLastLastCharUpper = isLastCharUpper;
35-
isLastCharUpper = character.toLocaleUpperCase(locale) === character && character.toLocaleLowerCase(locale) !== character;
35+
isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
3636
}
3737
}
3838

3939
return string;
4040
};
4141

42-
const preserveConsecutiveUppercase = input => {
42+
const preserveConsecutiveUppercase = (input, toLowerCase) => {
4343
LEADING_CAPITAL.lastIndex = 0;
4444

45-
return input.replace(LEADING_CAPITAL, m1 => m1.toLowerCase());
45+
return input.replace(LEADING_CAPITAL, m1 => toLowerCase(m1));
4646
};
4747

48-
const postProcess = (input, options) => {
48+
const postProcess = (input, toUpperCase) => {
4949
SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
5050
NUMBERS_AND_IDENTIFIER.lastIndex = 0;
5151

52-
return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => identifier.toLocaleUpperCase(options.locale))
53-
.replace(NUMBERS_AND_IDENTIFIER, m => m.toLocaleUpperCase(options.locale));
52+
return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier))
53+
.replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m));
5454
};
5555

5656
const camelCase = (input, options) => {
@@ -76,29 +76,36 @@ const camelCase = (input, options) => {
7676
return '';
7777
}
7878

79+
const toLowerCase = options.locale === false ?
80+
string => string.toLowerCase() :
81+
string => string.toLocaleLowerCase(options.locale);
82+
const toUpperCase = options.locale === false ?
83+
string => string.toUpperCase() :
84+
string => string.toLocaleUpperCase(options.locale);
85+
7986
if (input.length === 1) {
80-
return options.pascalCase ? input.toLocaleUpperCase(options.locale) : input.toLocaleLowerCase(options.locale);
87+
return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
8188
}
8289

83-
const hasUpperCase = input !== input.toLocaleLowerCase(options.locale);
90+
const hasUpperCase = input !== toLowerCase(input);
8491

8592
if (hasUpperCase) {
86-
input = preserveCamelCase(input, options.locale);
93+
input = preserveCamelCase(input, toLowerCase, toUpperCase);
8794
}
8895

8996
input = input.replace(LEADING_SEPARATORS, '');
9097

9198
if (options.preserveConsecutiveUppercase) {
92-
input = preserveConsecutiveUppercase(input);
99+
input = preserveConsecutiveUppercase(input, toLowerCase);
93100
} else {
94-
input = input.toLocaleLowerCase();
101+
input = toLowerCase(input);
95102
}
96103

97104
if (options.pascalCase) {
98-
input = input.charAt(0).toLocaleUpperCase(options.locale) + input.slice(1);
105+
input = toUpperCase(input.charAt(0)) + input.slice(1);
99106
}
100107

101-
return postProcess(input, options);
108+
return postProcess(input, toUpperCase);
102109
};
103110

104111
module.exports = camelCase;

readme.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ Preserve the consecutive uppercase characters: `foo-BAR` → `FooBAR`.
9494

9595
##### locale
9696

97-
Type: `string | string[]`\
97+
Type: `false | string | string[]`\
9898
Default: The host environment’s current locale.
9999

100100
The locale parameter indicates the locale to be used to convert to upper/lower case according to any locale-specific case mappings. If multiple locales are given in an array, the best available locale is used.
@@ -115,6 +115,20 @@ camelCase('lorem-ipsum', {locale: ['tr', 'TR', 'tr-TR']});
115115
//=> 'loremİpsum'
116116
```
117117

118+
Setting `locale: false` ignores the platform locale and uses the [Unicode Default Case Conversion](https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#simple-single-character-case-mapping) algorithm:
119+
120+
```js
121+
const camelCase = require('camelcase');
122+
123+
// On a platform with 'tr-TR'
124+
125+
camelCase('lorem-ipsum');
126+
//=> 'loremİpsum'
127+
128+
camelCase('lorem-ipsum', {locale: false});
129+
//=> 'loremIpsum'
130+
```
131+
118132
## camelcase for enterprise
119133

120134
Available as part of the Tidelift Subscription.

test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,43 @@ test('camelCase with locale option', t => {
204204
t.is(camelCase('ipsum-dolor', {pascalCase: true, locale: ['en-EN', 'en-GB']}), 'IpsumDolor');
205205
});
206206

207+
test('camelCase with disabled locale', t => {
208+
withLocaleCaseFunctionsMocked(() => {
209+
t.is(camelCase('lorem-ipsum', {locale: false}), 'loremIpsum');
210+
t.is(camelCase('ipsum-dolor', {pascalCase: true, locale: false}), 'IpsumDolor');
211+
t.is(camelCase('ipsum-DOLOR', {pascalCase: true, locale: false, preserveConsecutiveUppercase: true}), 'IpsumDOLOR');
212+
});
213+
});
214+
207215
test('invalid input', t => {
208216
t.throws(() => {
209217
camelCase(1);
210218
}, /Expected the input to be/);
211219
});
220+
221+
/* eslint-disable no-extend-native */
222+
const withLocaleCaseFunctionsMocked = fn => {
223+
const throwWhenBeingCalled = () => {
224+
throw new Error('Should not be called');
225+
};
226+
227+
const toLocaleUpperCase = Object.getOwnPropertyDescriptor(String.prototype, 'toLocaleUpperCase');
228+
const toLocaleLowerCase = Object.getOwnPropertyDescriptor(String.prototype, 'toLocaleLowerCase');
229+
230+
Object.defineProperty(String.prototype, 'toLocaleUpperCase', {
231+
...toLocaleUpperCase,
232+
value: throwWhenBeingCalled
233+
});
234+
Object.defineProperty(String.prototype, 'toLocaleLowerCase', {
235+
...toLocaleLowerCase,
236+
value: throwWhenBeingCalled
237+
});
238+
239+
try {
240+
fn();
241+
} finally {
242+
Object.defineProperty(String.prototype, 'toLocaleUpperCase', toLocaleUpperCase);
243+
Object.defineProperty(String.prototype, 'toLocaleLowerCase', toLocaleLowerCase);
244+
}
245+
};
246+
/* eslint-enable no-extend-native */

0 commit comments

Comments
 (0)