2021-11-08 22:58:20 +01:00
LOCALES_LIST = {
2021-12-22 00:52:08 +01:00
" ar " = > " العربية " , # Arabic
2022-10-30 13:46:28 +01:00
" bn " = > " বাংলা " , # Bengali
" ca " = > " Català " , # Catalan
2021-12-22 00:52:08 +01:00
" cs " = > " Čeština " , # Czech
" da " = > " Dansk " , # Danish
" de " = > " Deutsch " , # German
" el " = > " Ελληνικά " , # Greek
" en-US " = > " English " , # English
" eo " = > " Esperanto " , # Esperanto
" es " = > " Español " , # Spanish
2022-05-21 18:39:49 +02:00
" et " = > " Eesti keel " , # Estonian
2022-10-30 13:46:28 +01:00
" eu " = > " Euskara " , # Basque
2021-12-22 00:52:08 +01:00
" fa " = > " فارسی " , # Persian
" fi " = > " Suomi " , # Finnish
" fr " = > " Français " , # French
" he " = > " עברית " , # Hebrew
2022-05-02 19:34:08 +02:00
" hi " = > " हिन्दी " , # Hindi
2021-12-22 00:52:08 +01:00
" hr " = > " Hrvatski " , # Croatian
" hu-HU " = > " Magyar Nyelv " , # Hungarian
" id " = > " Bahasa Indonesia " , # Indonesian
" is " = > " Íslenska " , # Icelandic
" it " = > " Italiano " , # Italian
" ja " = > " 日本語 " , # Japanese
" ko " = > " 한국어 " , # Korean
" lt " = > " Lietuvių " , # Lithuanian
" nb-NO " = > " Norsk bokmål " , # Norwegian Bokmål
" nl " = > " Nederlands " , # Dutch
" pl " = > " Polski " , # Polish
" pt " = > " Português " , # Portuguese
" pt-BR " = > " Português Brasileiro " , # Portuguese (Brazil)
" pt-PT " = > " Português de Portugal " , # Portuguese (Portugal)
" ro " = > " Română " , # Romanian
2022-03-13 06:53:27 +01:00
" ru " = > " Русский " , # Russian
2022-10-30 13:46:28 +01:00
" si " = > " සිංහල " , # Sinhala
" sk " = > " Slovenčina " , # Slovak
2022-05-04 22:36:31 +02:00
" sl " = > " Slovenščina " , # Slovenian
2022-02-22 00:17:18 +01:00
" sq " = > " Shqip " , # Albanian
2022-03-13 06:53:27 +01:00
" sr " = > " Srpski (latinica) " , # Serbian (Latin)
" sr_Cyrl " = > " Српски (ћирилица) " , # Serbian (Cyrillic)
2021-12-22 00:52:08 +01:00
" sv-SE " = > " Svenska " , # Swedish
" tr " = > " Türkçe " , # Turkish
" uk " = > " Українська " , # Ukrainian
" vi " = > " Tiếng Việt " , # Vietnamese
" zh-CN " = > " 汉语 " , # Chinese (Simplified)
" zh-TW " = > " 漢語 " , # Chinese (Traditional)
2021-03-22 22:49:06 +01:00
}
2021-11-08 22:58:20 +01:00
LOCALES = load_all_locales ( )
2021-10-21 21:30:49 +02:00
CONTENT_REGIONS = {
" AE " , " AR " , " AT " , " AU " , " AZ " , " BA " , " BD " , " BE " , " BG " , " BH " , " BO " , " BR " , " BY " ,
" CA " , " CH " , " CL " , " CO " , " CR " , " CY " , " CZ " , " DE " , " DK " , " DO " , " DZ " , " EC " , " EE " ,
" EG " , " ES " , " FI " , " FR " , " GB " , " GE " , " GH " , " GR " , " GT " , " HK " , " HN " , " HR " , " HU " ,
" ID " , " IE " , " IL " , " IN " , " IQ " , " IS " , " IT " , " JM " , " JO " , " JP " , " KE " , " KR " , " KW " ,
" KZ " , " LB " , " LI " , " LK " , " LT " , " LU " , " LV " , " LY " , " MA " , " ME " , " MK " , " MT " , " MX " ,
" MY " , " NG " , " NI " , " NL " , " NO " , " NP " , " NZ " , " OM " , " PA " , " PE " , " PG " , " PH " , " PK " ,
" PL " , " PR " , " PT " , " PY " , " QA " , " RO " , " RS " , " RU " , " SA " , " SE " , " SG " , " SI " , " SK " ,
" SN " , " SV " , " TH " , " TN " , " TR " , " TW " , " TZ " , " UA " , " UG " , " US " , " UY " , " VE " , " VN " ,
2021-10-21 22:38:49 +02:00
" YE " , " ZA " , " ZW " ,
2021-10-21 21:30:49 +02:00
}
2021-12-27 15:17:50 +01:00
# Enum for the different types of number formats
enum NumberFormatting
None # Print the number as-is
Separator # Use a separator for thousands
Short # Use short notation (k/M/B)
HtmlSpan # Surround with <span id="count"></span>
end
2021-11-08 22:58:20 +01:00
def load_all_locales
locales = { } of String = > Hash ( String , JSON :: Any )
LOCALES_LIST . each_key do | name |
locales [ name ] = JSON . parse ( File . read ( " locales/ #{ name } .json " ) ) . as_h
end
return locales
2018-12-20 22:32:09 +01:00
end
2024-02-16 03:44:40 +01:00
def translate ( locale : String ?, key : String , text : String | Hash ( String , String ) | Nil = nil ) : String
2021-11-25 19:46:34 +01:00
# Log a warning if "key" doesn't exist in en-US locale and return
# that key as the text, so this is more or less transparent to the user.
2021-11-21 01:46:35 +01:00
if ! LOCALES [ " en-US " ] . has_key? ( key )
LOGGER . warn ( " i18n: Missing translation key \" #{ key } \" " )
2021-11-25 19:46:34 +01:00
return key
2021-11-21 01:46:35 +01:00
end
2018-12-20 22:32:09 +01:00
2021-11-08 23:52:55 +01:00
# Default to english, whenever the locale doesn't exist,
# or the key requested has not been translated
if locale && LOCALES . has_key? ( locale ) && LOCALES [ locale ] . has_key? ( key )
raw_data = LOCALES [ locale ] [ key ]
else
raw_data = LOCALES [ " en-US " ] [ key ]
end
case raw_data
when . as_h?
# Init
translation = " "
match_length = 0
2019-04-28 21:50:17 +02:00
2022-01-20 17:17:22 +01:00
raw_data . as_h . each do | hash_key , value |
2024-02-16 03:44:40 +01:00
if text . is_a? ( String )
if md = text . try & . match ( / #{ hash_key } / )
if md [ 0 ] . size >= match_length
translation = value . as_s
match_length = md [ 0 ] . size
end
2019-04-28 21:50:17 +02:00
end
end
end
2021-11-08 23:52:55 +01:00
when . as_s?
translation = raw_data . as_s
else
raise " Invalid translation \" #{ raw_data } \" "
2018-12-20 22:32:09 +01:00
end
2024-02-16 03:44:40 +01:00
if text . is_a? ( String )
2018-12-20 22:32:09 +01:00
translation = translation . gsub ( " `x` " , text )
2024-02-16 03:44:40 +01:00
elsif text . is_a? ( Hash ( String , String ) )
2024-01-30 03:40:25 +01:00
# adds support for multi string interpolation. Based on i18next https://www.i18next.com/translation-function/interpolation#basic
2024-02-16 03:44:40 +01:00
text . each_key do | hash_key |
translation = translation . gsub ( " {{ #{ hash_key } }} " , text [ hash_key ] )
2024-01-30 03:40:25 +01:00
end
2018-12-20 22:32:09 +01:00
end
return translation
end
2019-04-28 21:56:06 +02:00
2021-12-27 15:17:50 +01:00
def translate_count ( locale : String , key : String , count : Int , format = NumberFormatting :: None ) : String
2021-12-21 23:10:03 +01:00
# Fallback on english if locale doesn't exist
locale = " en-US " if ! LOCALES . has_key? ( locale )
# Retrieve suffix
suffix = I18next :: Plurals :: RESOLVER . get_suffix ( locale , count )
plural_key = key + suffix
if LOCALES [ locale ] . has_key? ( plural_key )
translation = LOCALES [ locale ] [ plural_key ] . as_s
else
# Try #1: Fallback to singular in the same locale
singular_suffix = I18next :: Plurals :: RESOLVER . get_suffix ( locale , 1 )
if LOCALES [ locale ] . has_key? ( key + singular_suffix )
translation = LOCALES [ locale ] [ key + singular_suffix ] . as_s
2022-01-05 23:44:36 +01:00
elsif locale != " en-US "
# Try #2: Fallback to english
2021-12-21 23:10:03 +01:00
translation = translate_count ( " en-US " , key , count )
2022-01-05 23:44:36 +01:00
else
2022-02-07 13:57:14 +01:00
# Return key if we're already in english, as the translation is missing
2022-01-05 23:44:36 +01:00
LOGGER . warn ( " i18n: Missing translation key \" #{ key } \" " )
return key
2021-12-21 23:10:03 +01:00
end
end
2021-12-27 15:17:50 +01:00
case format
when . separator? then count_txt = number_with_separator ( count )
when . short? then count_txt = number_to_short_text ( count )
when . html_span? then count_txt = " <span id= \" count \" > " + count . to_s + " </span> "
else count_txt = count . to_s
end
return translation . gsub ( " {{count}} " , count_txt )
2021-12-21 23:10:03 +01:00
end
2021-11-08 23:52:55 +01:00
def translate_bool ( locale : String ?, translation : Bool )
2019-04-28 21:56:06 +02:00
case translation
when true
return translate ( locale , " Yes " )
when false
return translate ( locale , " No " )
end
end
2023-04-18 00:04:49 +02:00
def locale_is_rtl? ( locale : String ?)
# Fallback to en-US
return false if locale . nil?
# Arabic, Persian, Hebrew
# See https://en.wikipedia.org/wiki/Right-to-left_script#List_of_RTL_scripts
return { " ar " , " fa " , " he " } . includes? locale
end