module ActiveSupport::Inflector

def transliterate(string, replacement = "?", locale: nil)

Other encodings will raise an ArgumentError.
Transliteration is restricted to UTF-8, US-ASCII, and GB18030 strings.

# => "Juergen"
transliterate('Jürgen', locale: :de)

# => "Jurgen"
transliterate('Jürgen', locale: :en)

Now you can have different transliterations for each locale:

})
}
rule: ->(string) { MyTransliterator.transliterate(string) }
transliterate: {
I18n.backend.store_translations(:de, i18n: {

complex requirements, a Proc:
maps characters to ASCII approximations as shown above, or, for more
The value for i18n.transliterate.rule can be a simple Hash that

})
}
}
'ö' => 'oe'
'ü' => 'ue',
rule: {
transliterate: {
I18n.backend.store_translations(:de, i18n: {
# Or set them using Ruby

ö: "oe"
ü: "ue"
rule:
transliterate:
i18n:
# Store the transliterations in locales/de.yml

them as the i18n.transliterate.rule i18n key:
In order to make your custom transliterations available, you must set

to ASCII.
and "ö" to "ue" and "oe", or to add support for transliterating Russian
locale. This can be useful, for example, to transliterate German's "ü"
This method is I18n aware, so you can set up custom approximations for a

e.g, "ø", "ñ", "é", "ß", etc.
Default approximations are provided for Western/Latin characters,

# => "AEroskobing"
transliterate('Ærøskøbing')

exists, a replacement character which defaults to "?".
Replaces non-ASCII characters with an ASCII approximation, or if none
def transliterate(string, replacement = "?", locale: nil)
  string = string.dup if string.frozen?
  raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
  raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding)
  input_encoding = string.encoding
  # US-ASCII is a subset of UTF-8 so we'll force encoding as UTF-8 if
  # US-ASCII is given. This way we can let tidy_bytes handle the string
  # in the same way as we do for UTF-8
  string.force_encoding(Encoding::UTF_8) if string.encoding == Encoding::US_ASCII
  # GB18030 is Unicode compatible but is not a direct mapping so needs to be
  # transcoded. Using invalid/undef :replace will result in loss of data in
  # the event of invalid characters, but since tidy_bytes will replace
  # invalid/undef with a "?" we're safe to do the same beforehand
  string.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace) if string.encoding == Encoding::GB18030
  transliterated = I18n.transliterate(
    ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
    replacement: replacement,
    locale: locale
  )
  # Restore the string encoding of the input if it was not UTF-8.
  # Apply invalid/undef :replace as tidy_bytes does
  transliterated.encode!(input_encoding, invalid: :replace, undef: :replace) if input_encoding != transliterated.encoding
  transliterated
end