diff --git a/.github/workflows/intl4x.yml b/.github/workflows/intl4x.yml index 13430fbd..d54e48c3 100644 --- a/.github/workflows/intl4x.yml +++ b/.github/workflows/intl4x.yml @@ -120,7 +120,7 @@ jobs: cd ffi/dart dart pub get cd ../.. - dart run ffi/dart/tool/build_libs.dart bin/linux_x64 linux_x64 dynamic icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,compiled_data + dart run ffi/dart/tool/build_libs.dart bin/linux_x64 linux_x64 dynamic icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap,compiled_data - name: Build Mac if: matrix.os == 'macos-latest' @@ -132,7 +132,7 @@ jobs: cd ffi/dart dart pub get cd ../.. - dart run ffi/dart/tool/build_libs.dart bin/macos_arm64 macos_arm64 dynamic icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,compiled_data + dart run ffi/dart/tool/build_libs.dart bin/macos_arm64 macos_arm64 dynamic icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap,compiled_data - name: Build Windows if: matrix.os == 'windows-latest' @@ -144,7 +144,7 @@ jobs: cd ffi/dart dart pub get cd ../.. - dart run ffi/dart/tool/build_libs.dart bin/windows_x64 windows_x64 dynamic icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,compiled_data + dart run ffi/dart/tool/build_libs.dart bin/windows_x64 windows_x64 dynamic icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap,compiled_data - run: echo "LOCAL_ICU4X_BINARY=$(realpath submodules/icu4x/bin/linux_x64)" >> $GITHUB_ENV if: matrix.os == 'ubuntu-latest' diff --git a/.github/workflows/intl4x_artifacts.yml b/.github/workflows/intl4x_artifacts.yml index d318c0a9..d5a738fe 100644 --- a/.github/workflows/intl4x_artifacts.yml +++ b/.github/workflows/intl4x_artifacts.yml @@ -68,14 +68,14 @@ jobs: cd ffi/dart dart pub get cd ../.. - dart run ffi/dart/tool/build_libs.dart bin/android_arm_${{ matrix.compiletype }} android_arm ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/android_arm64_${{ matrix.compiletype }} android_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/android_ia32_${{ matrix.compiletype }} android_ia32 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/android_x64_${{ matrix.compiletype }} android_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/linux_arm_${{ matrix.compiletype }} linux_arm ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/linux_arm64_${{ matrix.compiletype }} linux_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/linux_riscv64_${{ matrix.compiletype }} linux_riscv64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/linux_x64_${{ matrix.compiletype }} linux_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/android_arm_${{ matrix.compiletype }} android_arm ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/android_arm64_${{ matrix.compiletype }} android_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/android_ia32_${{ matrix.compiletype }} android_ia32 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/android_x64_${{ matrix.compiletype }} android_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/linux_arm_${{ matrix.compiletype }} linux_arm ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/linux_arm64_${{ matrix.compiletype }} linux_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/linux_riscv64_${{ matrix.compiletype }} linux_riscv64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/linux_x64_${{ matrix.compiletype }} linux_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} - name: Build Linux data if: matrix.os == 'ubuntu-latest' && matrix.compiletype == 'static' @@ -105,11 +105,11 @@ jobs: cd ffi/dart dart pub get cd ../.. - dart run ffi/dart/tool/build_libs.dart bin/ios_arm_${{ matrix.compiletype }} ios_arm ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/ios_arm64_${{ matrix.compiletype }} ios_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/ios_x64_${{ matrix.compiletype }} ios_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/macos_arm64_${{ matrix.compiletype }} macos_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/macos_x64_${{ matrix.compiletype }} macos_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/ios_arm_${{ matrix.compiletype }} ios_arm ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/ios_arm64_${{ matrix.compiletype }} ios_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/ios_x64_${{ matrix.compiletype }} ios_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/macos_arm64_${{ matrix.compiletype }} macos_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/macos_x64_${{ matrix.compiletype }} macos_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} - name: Build Mac data if: matrix.os == 'macos-latest' && matrix.compiletype == 'static' @@ -134,9 +134,9 @@ jobs: cd ffi/dart dart pub get cd ../.. - dart run ffi/dart/tool/build_libs.dart bin/windows_arm64_${{ matrix.compiletype }} windows_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/windows_ia32_${{ matrix.compiletype }} windows_ia32 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} - dart run ffi/dart/tool/build_libs.dart bin/windows_x64_${{ matrix.compiletype }} windows_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/windows_arm64_${{ matrix.compiletype }} windows_arm64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/windows_ia32_${{ matrix.compiletype }} windows_ia32 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} + dart run ffi/dart/tool/build_libs.dart bin/windows_x64_${{ matrix.compiletype }} windows_x64 ${{ matrix.compiletype }} icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,experimental_components,icu_casemap${{ env.DATA }} - name: Build Windows data if: matrix.os == 'windows-latest' && matrix.compiletype == 'static' diff --git a/pkgs/intl4x/CHANGELOG.md b/pkgs/intl4x/CHANGELOG.md index 594384b6..29bfb053 100644 --- a/pkgs/intl4x/CHANGELOG.md +++ b/pkgs/intl4x/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.2-wip + +- Add case mapping functionality. + ## 0.10.1 - Upgrade to new artifacts. diff --git a/pkgs/intl4x/hook/build.dart b/pkgs/intl4x/hook/build.dart index e19792a9..bd8ea94e 100644 --- a/pkgs/intl4x/hook/build.dart +++ b/pkgs/intl4x/hook/build.dart @@ -191,21 +191,23 @@ Future buildLib(BuildConfig config, String workingDirectory) async { } final tempDir = await Directory.systemTemp.createTemp(); - final stdFeatures = [ - 'icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals', + final commonFeatures = [ + 'icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals,icu_casemap', 'compiled_data', 'buffer_provider', + 'experimental_components', + ]; + + final stdFeatures = [ + ...commonFeatures, 'logging', 'simple_logger', - 'experimental_components', ]; final noStdFeatures = [ - 'icu_collator,icu_datetime,icu_list,icu_decimal,icu_plurals', - 'compiled_data', - 'buffer_provider', + ...commonFeatures, + //noStdFeatures 'libc-alloc', 'panic-handler', - 'experimental_components', ]; final linkModeType = config.linkModePreference == LinkModePreference.static ? 'staticlib' diff --git a/pkgs/intl4x/lib/case_mapping.dart b/pkgs/intl4x/lib/case_mapping.dart new file mode 100644 index 00000000..9853c6ba --- /dev/null +++ b/pkgs/intl4x/lib/case_mapping.dart @@ -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 'src/case_mapping/case_mapping.dart'; +export 'src/locale/locale.dart'; + +extension CaseMappingWithIntl4X on String { + String toLocaleLowerCase(Locale locale) => + Intl(locale: locale).caseMapping.toLowerCase(this); + String toLocaleUpperCase(Locale locale) => + Intl(locale: locale).caseMapping.toUpperCase(this); +} diff --git a/pkgs/intl4x/lib/intl4x.dart b/pkgs/intl4x/lib/intl4x.dart index 32dc1912..54ecaa81 100644 --- a/pkgs/intl4x/lib/intl4x.dart +++ b/pkgs/intl4x/lib/intl4x.dart @@ -5,6 +5,8 @@ import 'collation.dart'; import 'display_names.dart'; import 'number_format.dart'; +import 'src/case_mapping/case_mapping.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'; @@ -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. diff --git a/pkgs/intl4x/lib/src/case_mapping/case_mapping.dart b/pkgs/intl4x/lib/src/case_mapping/case_mapping.dart new file mode 100644 index 00000000..3f818fcb --- /dev/null +++ b/pkgs/intl4x/lib/src/case_mapping/case_mapping.dart @@ -0,0 +1,33 @@ +// 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'; + +/// A locale-sensitive case mapper for transforming strings. +/// +/// This class provides methods to convert strings to lowercase or uppercase +/// based on the current locale. During testing, the input is returned +/// unchanged. +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); + } + } +} diff --git a/pkgs/intl4x/lib/src/case_mapping/case_mapping_4x.dart b/pkgs/intl4x/lib/src/case_mapping/case_mapping_4x.dart new file mode 100644 index 00000000..1b8a67a1 --- /dev/null +++ b/pkgs/intl4x/lib/src/case_mapping/case_mapping_4x.dart @@ -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()); +} diff --git a/pkgs/intl4x/lib/src/case_mapping/case_mapping_ecma.dart b/pkgs/intl4x/lib/src/case_mapping/case_mapping_ecma.dart new file mode 100644 index 00000000..9cbe01f3 --- /dev/null +++ b/pkgs/intl4x/lib/src/case_mapping/case_mapping_ecma.dart @@ -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()); +} diff --git a/pkgs/intl4x/lib/src/case_mapping/case_mapping_impl.dart b/pkgs/intl4x/lib/src/case_mapping/case_mapping_impl.dart new file mode 100644 index 00000000..3fd43b84 --- /dev/null +++ b/pkgs/intl4x/lib/src/case_mapping/case_mapping_impl.dart @@ -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, + ); +} diff --git a/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub.dart b/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub.dart new file mode 100644 index 00000000..d0e1fb3c --- /dev/null +++ b/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub.dart @@ -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.'); diff --git a/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub_4x.dart b/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub_4x.dart new file mode 100644 index 00000000..a1f4533f --- /dev/null +++ b/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub_4x.dart @@ -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.'); diff --git a/pkgs/intl4x/pubspec.yaml b/pkgs/intl4x/pubspec.yaml index e6db3305..b5a35d32 100644 --- a/pkgs/intl4x/pubspec.yaml +++ b/pkgs/intl4x/pubspec.yaml @@ -1,7 +1,7 @@ name: intl4x description: >- A lightweight modular library for internationalization (i18n) functionality. -version: 0.10.1 +version: 0.10.2-wip repository: https://github.com/dart-lang/i18n/tree/main/pkgs/intl4x platforms: web: diff --git a/pkgs/intl4x/test/case_mapping_test.dart b/pkgs/intl4x/test/case_mapping_test.dart new file mode 100644 index 00000000..0b95ac25 --- /dev/null +++ b/pkgs/intl4x/test/case_mapping_test.dart @@ -0,0 +1,25 @@ +// 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:intl4x/case_mapping.dart'; +import 'package:test/test.dart'; + +import 'utils.dart'; + +void main() { + testWithFormatting('test name', () { + const enUS = 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'); + } + }); +}