# RFC: Add locale for case insensitive grapheme functions

- Status: Implemented (PHP 8.5)
- Authors: Yuya Hamada, youkidearitai@gmail.com
- Date: 2025-06-07
- Version: 2.0
- Original: https://wiki.php.net/rfc/grapheme_add_locale_for_case_insensitive
- Page: https://externals.io/rfc/grapheme_add_locale_for_case_insensitive
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Votes

### A grapheme functions add locale and case insensitive (closed)

Yes: 7 · No: 0

Voters: dunglas (Yes), ericmann (Yes), galvao (Yes), girgias (Yes), kalle (Yes), nicolasgrekas (Yes), sergey (Yes)

## Discussions on the mailing list

- [\[RFC\]\[DISCUSSION\] Add $locale parameter for grapheme_* functions](https://externals.io/message/127669.md) — started on 2025-06-16, 2 messages
- [\[Voting\] Add locale for case insensitive grapheme functions](https://externals.io/message/127791.md) — started on 2025-06-30, 6 messages
- [\[DISCUSSION\] Add locale and strength for grapheme functions](https://externals.io/message/127961.md) — started on 2025-07-09, 4 messages
- [\[RFC\]\[VOTE\] grapheme_* functions add locale](https://externals.io/message/128224.md) — started on 2025-07-25, 2 messages

---

## Introduction

A grapheme functions is not locale dependency. This RFC is add locale parameter for grapheme case insensitive functions. That is more enhancements for Unicode can be expected.

- grapheme\_strpos
- grapheme\_stripos
- grapheme\_strrpos
- grapheme\_strripos
- grapheme\_strstr
- grapheme\_stristr
- grapheme\_levenshtein

By this RFC can cover locale. For example.

```
var_dump(grapheme_stripos("i", "\u{0130}", 0, "tr_TR")); // Result is 0
var_dump(grapheme_stripos("i", "\u{0130}", 0, "en_US")); // Result is false
```

## Proposal

Add a $locale parameter and $strength parameter in these functions.

```
function grapheme_strpos(string $haystack, string $needle, int $offset = 0, string $locale = ""): int|false {}
function grapheme_stripos(string $haystack, string $needle, int $offset = 0, string $locale = ""): int|false {}
function grapheme_strrpos(string $haystack, string $needle, int $offset = 0, string $locale = ""): int|false {}
function grapheme_strripos(string $haystack, string $needle, int $offset = 0, string $locale = ""): int|false {}
function grapheme_substr(string $string, int $offset, ?int $length = null, string $locale = ""): string|false {}
function grapheme_strstr(string $haystack, string $needle, bool $beforeNeedle = false, string $locale = ""): string|false {}
function grapheme_stristr(string $haystack, string $needle, bool $beforeNeedle = false, string $locale = ""): string|false {}
function grapheme_levenshtein(string $string1, string $string2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1, string $locale = ""): int|false {}
```

$locale is based on LDML. <https://www.unicode.org/reports/tr35>

Specifying strength can change the match for CJK characters, For example:

```
$nabe = '邊';
$nabe_E0101 = "邊\u{E0101}";
var_dump(grapheme_levenshtein($nabe, $nabe_E0101)); // result is 0
var_dump(grapheme_levenshtein($nabe, $nabe_E0101, locale: "ja_JP-u-ks-identic")); // result is 1
var_dump(grapheme_strpos($nabe, $nabe_E0101)); // result is 0
var_dump(grapheme_strpos($nabe, $nabe_E0101, locale: "ja_JP-u-ks-identic")); // result is false
```

If $locale is not valid, returns false and set `intl_error_code_set` and `intl_error_set_custom_msg`. Therefore, PHP userland can use `intl_get_error_code` and `intl_get_error_message` for reason.

```
$ sapi/cli/php -r 'var_dump(grapheme_levenshtein("abc", "def", locale: "defaaaaaaaaaaaaa"), intl_get_error_message());'
bool(false)
string(44) "Error on ucol_open: U_ILLEGAL_ARGUMENT_ERROR"
```

grapheme\_stri\* The strength of the function remains unchanged and UCOL\_SECONDARY is used.

```
var_dump(grapheme_stripos("邊", "邊\u{E0101}", locale: "ja_JP-u-ks-identic")); // result is 0 (matched) because strength is UCOL_SECONDARY.
```

The reason for removing $strength is to avoid complexity and because it can be specified with $locale.

## Backward Incompatible Changes

Nothing if added parameter is default values.

## Proposed PHP Version(s)

8.5

## RFC Impact

### To SAPIs

No effects.

### To Existing Extensions

No effects.

### To Opcache

No effects.

### New Constants

No effects.

## Open Issues

Nothing.

## Future Scope

This section details areas where the feature might be improved in future, but that are not currently proposed in this RFC.

## Proposed Voting Choices

**Vote: A grapheme functions add locale and case insensitive** — Yes: 7 · No: 0 (closed)

## Patches and Tests

<https://github.com/php/php-src/pull/18792>

## Implementation

<https://github.com/php/php-src/pull/18792>

## References

- <https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#full-language-specific-case-mapping>
- <https://www.unicode.org/reports/tr35>

## Rejected Features

Keep this updated with features that were discussed on the mail lists.
