Skip to content

Commit

Permalink
Add locale-sensitive casing
Browse files Browse the repository at this point in the history
  • Loading branch information
mosuem committed Aug 28, 2024
1 parent 4796ea1 commit 3cd946d
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 0 deletions.
15 changes: 15 additions & 0 deletions pkgs/intl4x/lib/case_mapping.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'intl4x.dart';

export 'intl4x.dart';
export 'src/case_mapping/case_mapping.dart';

extension CaseMappingWithIntl4X on String {
String toLocaleLowerCase(Locale locale) =>
Intl(locale: locale).caseMapping.toLowerCase(this);
String toLocaleUpperCase(Locale locale) =>
Intl(locale: locale).caseMapping.toLowerCase(this);
}
5 changes: 5 additions & 0 deletions pkgs/intl4x/lib/intl4x.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'case_mapping.dart';
import 'collation.dart';
import 'display_names.dart';
import 'number_format.dart';
import 'src/case_mapping/case_mapping_impl.dart';
import 'src/collation/collation_impl.dart';
import 'src/data.dart';
import 'src/datetime_format/datetime_format.dart';
Expand Down Expand Up @@ -82,6 +84,9 @@ class Intl {
localeMatcher, ecmaPolicy),
);

CaseMapping get caseMapping => CaseMapping(
CaseMappingImpl.build(locale, data, localeMatcher, ecmaPolicy));

/// Construct an [Intl] instance providing the current [locale] and the
/// [ecmaPolicy] defining which locales should fall back to the browser
/// provided functions.
Expand Down
28 changes: 28 additions & 0 deletions pkgs/intl4x/lib/src/case_mapping/case_mapping.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../test_checker.dart';
import 'case_mapping_impl.dart';

class CaseMapping {
final CaseMappingImpl _caseMappingImpl;

const CaseMapping(this._caseMappingImpl);

String toLowerCase(String input) {
if (isInTest) {
return input;
} else {
return _caseMappingImpl.toLowerCase(input);
}
}

String toUpperCase(String input) {
if (isInTest) {
return input;
} else {
return _caseMappingImpl.toUpperCase(input);
}
}
}
32 changes: 32 additions & 0 deletions pkgs/intl4x/lib/src/case_mapping/case_mapping_4x.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../bindings/lib.g.dart' as icu;
import '../data.dart';
import '../data_4x.dart';
import '../locale/locale.dart';
import '../locale/locale_4x.dart';
import 'case_mapping_impl.dart';

CaseMappingImpl getCaseMapping4X(
Locale locale,
Data data,
Null _,
) =>
CaseMapping4X(locale, data);

class CaseMapping4X extends CaseMappingImpl {
final icu.CaseMapper _caseMapper;

CaseMapping4X(super.locale, Data data)
: _caseMapper = icu.CaseMapper(data.to4X());

@override
String toLowerCase(String input) =>
_caseMapper.lowercase(input, locale.to4X());

@override
String toUpperCase(String input) =>
_caseMapper.uppercase(input, locale.to4X());
}
39 changes: 39 additions & 0 deletions pkgs/intl4x/lib/src/case_mapping/case_mapping_ecma.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:js_interop';

import '../locale/locale.dart';
import '../options.dart';
import 'case_mapping_impl.dart';

CaseMappingImpl? getCaseMappingECMA(
Locale locale,
Null __,
LocaleMatcher _,
) =>
_CaseMappingECMA.tryToBuild(locale);

extension on JSString {
@JS('String.toLocaleUpperCase')
external String toLocaleUpperCase(String locale);
@JS('String.toLocaleLowerCase')
external String toLocaleLowerCase(String locale);
}

class _CaseMappingECMA extends CaseMappingImpl {
_CaseMappingECMA(super.locale);

static CaseMappingImpl? tryToBuild(
Locale locale,
) =>
_CaseMappingECMA(locale);
@override
String toUpperCase(String input) =>
input.toJS.toLocaleUpperCase(locale.toLanguageTag());

@override
String toLowerCase(String input) =>
input.toJS.toLocaleLowerCase(locale.toLanguageTag());
}
40 changes: 40 additions & 0 deletions pkgs/intl4x/lib/src/case_mapping/case_mapping_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:meta/meta.dart' show ResourceIdentifier;

import '../../ecma_policy.dart';
import '../data.dart';
import '../ecma/ecma_policy.dart';
import '../locale/locale.dart';
import '../options.dart';
import '../utils.dart';
import 'case_mapping_stub.dart' if (dart.library.js) 'case_mapping_ecma.dart';
import 'case_mapping_stub_4x.dart' if (dart.library.io) 'case_mapping_4x.dart';

abstract class CaseMappingImpl {
final Locale locale;

CaseMappingImpl(this.locale);

String toLowerCase(String input);
String toUpperCase(String input);

@ResourceIdentifier('CaseMapping')
static CaseMappingImpl build(
Locale locales,
Data data,
LocaleMatcher localeMatcher,
EcmaPolicy ecmaPolicy,
) =>
buildFormatter(
locales,
data,
null,
localeMatcher,
ecmaPolicy,
getCaseMappingECMA,
getCaseMapping4X,
);
}
14 changes: 14 additions & 0 deletions pkgs/intl4x/lib/src/case_mapping/case_mapping_stub.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../locale/locale.dart';
import '../options.dart';
import 'case_mapping_impl.dart';

CaseMappingImpl? getCaseMappingECMA(
Locale locale,
Null _,
LocaleMatcher __,
) =>
throw UnimplementedError('Cannot use ECMA outside of web environments.');
14 changes: 14 additions & 0 deletions pkgs/intl4x/lib/src/case_mapping/case_mapping_stub_4x.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../data.dart';
import '../locale/locale.dart';
import 'case_mapping_impl.dart';

CaseMappingImpl getCaseMapping4X(
Locale locale,
Data data,
Null _,
) =>
throw UnimplementedError('Cannot use ICU4X in web environments.');
28 changes: 28 additions & 0 deletions pkgs/intl4x/test/ecma/case_mapping_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

@TestOn('browser')
library;

import 'package:intl4x/case_mapping.dart';
import 'package:test/test.dart';

import '../utils.dart';

void main() {
testWithFormatting('test name', () {
const enUS = const Locale(language: 'en', region: 'US');
expect('İstanbul'.toLocaleLowerCase(enUS), 'i̇stanbul');
expect('ALPHABET'.toLocaleLowerCase(enUS), 'alphabet');

expect('\u0130'.toLocaleLowerCase(const Locale(language: 'tr')), 'i');
expect('\u0130'.toLocaleLowerCase(enUS), isNot('i'));

final locales = ['tr', 'TR', 'tr-TR', 'tr-u-co-search', 'tr-x-turkish']
.map(Locale.parse);
for (final locale in locales) {
expect('\u0130'.toLocaleLowerCase(locale), 'i');
}
});
}

0 comments on commit 3cd946d

Please sign in to comment.