Thursday, July 9, 2020

#1 2020-03-10 10:51:31 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 4544

localized strings from js resource files

The tool delivered to localize strings apply only on files named xxx.strings but, as nothing is really simple with Apple, some strings are stored in files named yyy.js.
In High Sierra there are several sets of resources using this format:

Macintosh HD:Applications:Safari.app: table:localizedStrings.js
Macintosh HD:Library:Widgets:Calculator.wdgt: table:localizedStrings.js
Macintosh HD:Library:Widgets:Calendar.wdgt: table:localizedStrings.js
Macintosh HD:Library:Widgets:Contacts.wdgt: table:localizedStrings.js
Macintosh HD:Library:Widgets:Dictionary.wdgt: table:localizedStrings.js
Macintosh HD:Library:Widgets:Stickies.wdgt: table:localizedStrings.js
Macintosh HD:Library:Widgets:Stocks.wdgt: table:localizedStrings.js
Macintosh HD:Library:Widgets:Unit Converter.wdgt: table:localizedStrings.js
Macintosh HD:Library:Widgets:Weather.wdgt: table:localizedStrings.js
Macintosh HD:Library:Widgets:Web Clip.wdgt: table:localizedStrings.js
Macintosh HD:Library:Widgets:World Clock.wdgt: table:localizedStrings.js
Macintosh HD:System:Library:CoreServices:HelpViewer.app: table:localizedStrings.js
Macintosh HD:System:Library:Frameworks:WebKit.framework:Versions:A:Frameworks:WebCore.framework: table:mediaControlsLocalizedStrings.js
Macintosh HD:System:Library:Frameworks:WebKit.framework:Versions:A:Frameworks:WebCore.framework: table:modern-media-controls-localized-strings.js
Macintosh HD:System:Library:PrivateFrameworks:Safari.framework: table:localizedStrings.js
Macintosh HD:System:Library:PrivateFrameworks:SafariShared.framework: table:WBSLocalizedStrings.js don't ask what need for this 'special' name
Macintosh HD:System:Library:PrivateFrameworks:WebInspectorUI.framework: table:localizedStrings.js

Macintosh HD:Library:Widgets:Movies.wdgt: table:localizedStrings.js
For this late one, in High Sierra there is only an English file so it's not really useful.

As imagination of Apple engineers has no limit, they use (at least) four different syntaxes and two encodings utf8 or ut16.

Below is a script supposed to extract localized strings from these different formats.

Applescript:

----------------------------------------------------------------
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
----------------------------------------------------------------

-- localize strings defined in yyy.js files

-- use quote & ":"
set key_loc1 to my shared((path to library folder from system domain as text) & "Frameworks:WebKit.framework:Versions:A:Frameworks:WebCore.framework:Versions:A:Resources:", "Display Picture in Picture")
-- use "':"
set key_loc2 to my shared((path to library folder from system domain as text) & "Frameworks:WebKit.framework:Versions:A:Frameworks:WebCore.framework:Versions:A:Resources:", "This video is playing in picture in picture.")
-- use quote & "]"
set key_loc3 to my shared((path to applications folder as text) & "Safari.app:Contents:Resources:", "Updates are available for one or more of your extensions. To install an update, click its Update button.")
-- use "']"
set key_loc4 to my shared((path to library folder from local domain as text) & "Widgets:Contacts.wdgt:", "No Matching Cards")
-- use "']"
set key_loc5 to my shared((path to library folder from local domain as text) & "Widgets:Contacts.wdgt:", "McCoy Tyner")

{key_loc1, key_loc2, key_loc3, key_loc4, key_loc5}

#=====

on shared(begPath, theKey)
   
   if begPath contains ":WebKit.framework:" then
       set tableNames to {"modern-media-controls-localized-strings.js", "mediaControlsLocalizedStrings.js"}
   else if begPath contains ":Safari.framework:" then
       set tableNames to {"WBSLocalizedStrings.js"}
   else
       set tableNames to {"localizedStrings.js"}
   end if
   
   repeat with tableName in tableNames
       set key_loc to (my localize:theKey fromTable:tableName inBundle:begPath)
       if key_loc is not missing value then exit repeat
   end repeat
   if key_loc is missing value then set key_loc to "the key “" & theKey & "” is unavaible in this file!"
   return key_loc
end shared

#=====

on localize:the_Key fromTable:theTable inBundle:begPath
   set thisLocale to current application's NSLocale's currentLocale()
   set langX to thisLocale's localeIdentifier as string --> "fr_FR"
   set lang2 to text 1 thru 2 of langX
   if langX starts with "zh_Hans" then # "zh-Hans"
       set path2lproj to begPath & "zh_CN.lproj"
   else if langX starts with "zh_Hant" then # "zh-Hant"
       set path2lproj to begPath & "zh_TW.lproj"
   else if langX starts with "pt_PT" then -- "pt_PT"
       set path2lproj to begPath & "pt_PT.lproj"
   else if langX starts with "pt_BR" then -- "pt_BR"
       set path2lproj to begPath & "pt_BR.lproj"
   else if lang2 is in {"ar", "ca", "cs", "da", "el", "fi", "he", "hr", "hu", "id", "ko", "ms", "pl", "pt", "ro", "ru", "sk", "sv", "th", "tr", "uk", "vi"} then
       set path2lproj to begPath & lang2 & ".lproj"
   else if lang2 is "de" then # {"de", "de-AT", "de-CH"}
       set path2lproj to begPath & "German.lproj"
   else if lang2 is "en" then # {"en", "en_AU", "en_CA", "en_GB", "en_US"}
       set path2lproj to begPath & "English.lproj"
   else if lang2 is "es" then # {"es", "es_419", "es_ES", "es_MX"}
       set path2lproj to begPath & "Spanish.lproj"
   else if lang2 is "fr" then # {"fr_FR", "fr_CA", "fr_CH"}
       tell application "System Events"
           if exists folder (begPath & "French.lproj") then
               set path2lproj to begPath & "French.lproj"
           else
               set path2lproj to begPath & "fr.lproj"
           end if
       end tell
   else if lang2 is "it" then
       set path2lproj to begPath & "Italian.lproj"
   else if lang2 is "ja" then
       set path2lproj to begPath & "Japanese.lproj"
   else if lang2 is "nb" then
       set path2lproj to begPath & "no.lproj"
   else if lang2 is "nl" then # {"nl", "nl_BE"}
       set path2lproj to begPath & "Dutch.lproj"
   end if
   
   set theFile to (path2lproj & ":" & theTable) as «class furl»
   
   try
       set itsText to read theFile as «class utf8»
   on error
       set itsText to read theFile as «class ut16»
   end try
   
   set theTable to contents of theTable -- must deref
   if theTable is "modern-media-controls-localized-strings.js" then
       set theDelim to quote & the_Key & quote & ":"
   else if theTable is "mediaControlsLocalizedStrings.js" then
       set theDelim to "'" & the_Key & "':"
   else
       if itsText contains "';" then
           set theDelim to "['" & the_Key & "']"
       else
           set theDelim to "[" & quote & the_Key & quote & "]"
       end if
   end if
   -- For info, in front of theDelim we may have "localizedStrings", "localizedControls","localizedFonts", "localizedContinentNames" or "localizedCityNames"
   
   if itsText does not contain theDelim then return missing value
   
   set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, theDelim}
   set l to text items of itsText
   set AppleScript's text item delimiters to oTIDs
   set key_loc to paragraph 1 of (item 2 of l)
   
   if theTable is "modern-media-controls-localized-strings.js" then
       set dels to {" " & quote, quote & ","}
   else if theTable is "mediaControlsLocalizedStrings.js" then
       set dels to {" '", "',"}
   else
       if key_loc contains "';" then
           set dels to {" '", "';"}
       else
           set dels to {" " & quote, quote & ";"}
       end if
   end if
   
   set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, dels}
   set l to text items of key_loc
   set AppleScript's text item delimiters to oTIDs
   return (item 2 of l) as string
end localize:fromTable:inBundle:

#=====

Maybe, one day, I would convert this code in a library so it would be easier to use it in our scripts.

Maybe too, I would scan Mojave and/or Catalina to check if they use the yyy.js format in other lprojs.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 10 mars 2020  17:48:07

Offline

 

#2 2020-03-14 09:24:04 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 4544

Re: localized strings from js resource files

After collecting infos about Mojave and Catalina, I updated my script.
As far as I know, at this time the only remaining problem is the way to search in the correct Chinese resources.

Applescript:

----------------------------------------------------------------
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
----------------------------------------------------------------

-- localize strings defined in yyy.js files
property testEnglish : true
-- true = force the scanned language to English
-- false = scan the current language

-- use quote & ":"
set key_loc1 to my shared((path to library folder from system domain as text) & "Frameworks:WebKit.framework:Versions:A:Frameworks:WebCore.framework:Versions:A:Resources:", "Display Picture in Picture")

-- use "':"
set key_loc2 to my shared((path to library folder from system domain as text) & "Frameworks:WebKit.framework:Versions:A:Frameworks:WebCore.framework:Versions:A:Resources:", "This video is playing in picture in picture.")

-- use quote & "]"
set key_loc3 to my shared((path to applications folder as text) & "Safari.app:Contents:Resources:", "Updates are available for one or more of your extensions. To install an update, click its Update button.")

-- use "']"
set key_loc4 to my shared((path to library folder from local domain as text) & "Widgets:Contacts.wdgt:", "No Matching Cards")

-- use "']"
set key_loc5 to my shared((path to library folder from local domain as text) & "Widgets:Contacts.wdgt:", "McCoy Tyner")

{key_loc1, key_loc2, key_loc3, key_loc4, key_loc5}

#=====

on shared(begPath, theKey)
   
   if begPath contains ":WebKit.framework:" then
       set tableNames to {"modern-media-controls-localized-strings.js", "mediaControlsLocalizedStrings.js"}
   else if begPath contains ":Safari.framework:" then
       set tableNames to {"WBSLocalizedStrings.js"} -- what need for this 'special' name ?
   else
       set tableNames to {"localizedStrings.js"}
   end if
   
   repeat with tableName in tableNames
       set key_loc to (my localize:theKey fromTable:tableName inBundle:begPath)
       if key_loc is not missing value then exit repeat
   end repeat
   if key_loc is missing value then set key_loc to "the key “" & theKey & "” is unavaible in this file!"
   return key_loc
end shared

#=====

on localize:the_Key fromTable:theTable inBundle:begPath
   -- set languageAvailables to current application's NSLocale's availableLocaleIdentifiers() as list
   set thisLocale to current application's NSLocale's currentLocale()
   set langX to thisLocale's localeIdentifier as string --> "fr_FR"
   set lang2 to thisLocale's languageCode as text --> "fr"
   if testEnglish then
       set langX to "en_GB" -- used for tests
       set lang2 to "en"
   end if
   
   tell application "System Events"
       if lang2 is in {"ar", "ca", "cs", "da", "el", "fi", "he", "hi", "hr", "hu", "id", "ko", "ms", "pl", "ro", "ru", "sk", "sv", "th", "tr", "uk", "vi"} then
           set path2lproj to begPath & lang2 & ".lproj"
           
       else if lang2 is "de" then
           if (langX starts with "de_AT") and (exists folder (begPath & "de_AT.lproj")) then
               set path2lproj to begPath & "de_AT.lproj"
           else if (langX starts with "de_CH") and (exists folder (begPath & "de_CH.lproj")) then
               set path2lproj to begPath & "de_CH.lproj"
           else if exists folder (begPath & "de.lproj") then
               set path2lproj to begPath & "dl.lproj"
           else
               set path2lproj to begPath & "German.lproj" -- used in High Sierra or Mojave, not used in Catalina
           end if
           
       else if lang2 is "en" then
           if (langX starts with "en_AU") and (exists folder (begPath & "en_AU.lproj")) then
               set path2lproj to begPath & "en_AU.lproj.lproj"
           else if (langX starts with "en_CA") and (exists folder (begPath & "en_CA.lproj")) then
               set path2lproj to begPath & "en_CA.lproj"
           else if (langX starts with "en_GB") and (exists folder (begPath & "en_GB.lproj")) then
               set path2lproj to begPath & "en_GB.lproj"
           else if (langX starts with "en_US") and (exists folder (begPath & "en_US.lproj")) then
               set path2lproj to begPath & "en_US.lproj"
           else if exists folder (begPath & "en.lproj") then
               set path2lproj to begPath & "en.lproj"
           else
               set path2lproj to begPath & "English.lproj" -- used in High Sierra or Mojave, not used in Catalina
           end if

       else if lang2 is "es" then
           if (langX starts with "es_41") and (exists folder (begPath & "es_419.lproj")) then
               set path2lproj to begPath & "es_419.lproj"
           else if (langX starts with "es_ES") and (exists folder (begPath & "es_ES.lproj")) then
               set path2lproj to begPath & "es_ES.lproj"
           else if (langX starts with "es_MX") and (exists folder (begPath & "es_MX.lproj")) then
               set path2lproj to begPath & "es_MX.lproj"
           else if exists folder (begPath & "es.lproj") then
               set path2lproj to begPath & "es.lproj"
           else
               set path2lproj to begPath & "Spanish.lproj" -- used in High Sierra or Mojave, not used in Catalina
           end if

       else if lang2 is "fr" then
           if (langX starts with "fr_CA") and (exists folder (begPath & "fr_CA.lproj")) then
               set path2lproj to begPath & "fr_CA.lproj"
           else if (langX starts with "fr_CH") and (exists folder (begPath & "fr_CH.lproj")) then
               set path2lproj to begPath & "fr_CH.lproj"
           else if exists folder (begPath & "fr.lproj") then
               set path2lproj to begPath & "fr.lproj"
           else
               set path2lproj to begPath & "French.lproj" -- used in High Sierra or Mojave, not used in Catalina
           end if

       else if lang2 is "it" then
           if exists folder (begPath & "it.lproj") then
               set path2lproj to begPath & "it.lproj"
           else
               set path2lproj to begPath & "Italian.lproj" -- used in High Sierra or Mojave, not used in Catalina
           end if

       else if lang2 is "ja" then
           if exists folder (begPath & "ja.lproj") then
               set path2lproj to begPath & "ja.lproj"
           else
               set path2lproj to begPath & "Japanese.lproj" -- used in High Sierra or Mojave, not used in Catalina
           end if

       else if ((lang2 is "nb") or (lang2 is "no")) then -- I'm not sure that we may get lang2 = "no"
           set path2lproj to begPath & "no.lproj"

       else if lang2 is "nl" then # {"nl", "nl_BE"}
           if (langX starts with "nl_BE") and (exists folder (begPath & "nl_BE.lproj")) then
               set path2lproj to begPath & "nl_BE.lproj"
           else if exists folder (begPath & "nl.lproj") then
               set path2lproj to begPath & "nl.lproj"
           else
               set path2lproj to begPath & "Dutch.lproj" -- used in High Sierra or Mojave, not used in Catalina
           end if

       else if lang2 is "pt" then
           if (langX starts with "pt_PT") and (exists folder (begPath & "pt_PT.lproj")) then
               set path2lproj to begPath & "pt_PT.lproj"
           else if (langX starts with "pt_BR") and (exists folder (begPath & "pt_BR.lproj")) then
               set path2lproj to begPath & "pt_BR.lproj"
           else
               set path2lproj to begPath & "pt.lproj"
           end if

       else if lang2 is "yue" then
           if langX starts with "yue_Hans" and (exists folder (begPath & "yue_CN.lproj")) then
               set path2lproj to begPath & "yue_CN.lproj"
           else if langX starts with "yue_Hant" and (exists folder (begPath & "yue_CN.lproj")) then
               set path2lproj to begPath & "yue_CN.lproj"
           else
               error "Which lproj is linked to the locale " & langX & " with your System ?"
           end if

       else if lang2 is "zh" then
           if langX contains "_CN" then
               set path2lproj to begPath & "zh_CN.lproj" -- used in High Sierra, Mojave or Catalina
           else if langX is "zh_Hant_TW" then
               set path2lproj to begPath & "zh_TW.lproj" -- used in High Sierra, Mojave or Catalina
           else if langX is "zh_Hans_HK" then
               if (exists folder (begPath & "zh_HK.lproj")) then
                   set path2lproj to begPath & "zh_HK.lproj" -- not used in High Sierra, used in Mojave or Catalina
               else
                   error "Which lproj is linked to the locale " & langX & " with High Sierra ?"
               end if
           else if langX is "zh_Hant_HK" then
               if (exists folder (begPath & "zh_HK.lproj")) then
                   set path2lproj to begPath & "zh_HK.lproj" -- not used in High Sierra, used in Mojave or Catalina
               else
                   error "Which lproj is linked to the locale " & langX & " with High Sierra ?"
               end if
           else if langX is "zh_Hans" then
               error "Which lproj is linked to the locale " & langX & " with your System ?"
           else if langX is "zh_Hans_HK" then
               error "Which lproj is linked to the locale " & langX & " with your System ?"
           else if langX is "zh_Hans_MO" then
               error "Which lproj is linked to the locale " & langX & " with your System ?"
           else if langX is "zh_Hans_SG" then
               error "Which lproj is linked to the locale " & langX & " with your System ?"
           else if langX is "zh_Hant" then
               error "Which lproj is linked to the locale " & langX & " with your System ?"
           else if langX is "zh_Hant_MO" then
               error "Which lproj is linked to the locale " & langX & " with your System ?"
           end if
       else
           -- 'unidentified' language, default to English
           if exists folder (begPath & "en.lproj") then
               set path2lproj to begPath & "en.lproj"
           else
               set path2lproj to begPath & "English.lproj" -- used in High Sierra or Mojave, not used in Catalina
           end if
       end if
   end tell
   set theFile to (path2lproj & ":" & theTable) as «class furl»
   
   try
       set itsText to read theFile as «class utf8»
   on error
       set itsText to read theFile as «class ut16»
   end try
   (*
   -- As far as I know there is no multilines string in the js resources
   *)

   if itsText does not contain the_Key then return missing value -- no need to search more if the_Key is not available, possibly as a substring of an item
   set delims to {quote & the_Key & quote, "'" & the_Key & "'"}
   set l to my decoupe(itsText, delims)
   set cntl to count l
   if cntl = 1 then
       set key_loc to missing value -- no match
   else if cntl = 3 then
       set key_loc to the_Key -- we are running on a system using an English idiom
       log ">>>>>>>>>>>> " & lang2
   else
       set maybe to item 2 of l
       set delims to {quote, "'", quote & ",", "',", quote & ";", "';"}
       try
           set key_loc to item 2 of my decoupe(maybe, delims)
       on error errMsg number errNbr
           set key_loc to the_Key & " issued the error #" & errNbr & tab & errMsg
       end try
   end if
   return key_loc
end localize:fromTable:inBundle:

#=====

on decoupe(t, d)
   local oTIDs, l
   set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d}
   set l to text items of t
   set AppleScript's text item delimiters to oTIDs
   return l
end decoupe

#=====

As you may see, I am unable to link correctly the available lprojs - zh_CN.lproj, zh_HK.lproj and zh_TW.lproj under Mojave and Catalina but only zh_CN.lproj and zh_TW.lproj under High Sierra - and the known locale identifiers.
Under High Sierra, these identifiers are : "zh", "zh_Hans", "zh_Hans_CN", "zh_Hans_HK", "zh_Hans_MO", "zh_Hans_SG", "zh_Hant", "zh_Hant_HK", "zh_Hant_MO", "zh_Hant_TW"
and maybe they are other ones in Mojave or Catalina.

Same kind of problem with the locales "yue", "yue_Hans", "yue_Hans_CN", "yue_Hant", "yue_Hant_HK".
When I don't have a logical link, the script issue an error.

Of course I would be interested to learn the way identifiers are distributed among the lprojs.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 14 mars 2020  16:19:12

Last edited by Yvan Koenig (2020-03-14 12:19:48 pm)

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)